Installation

  1. Install R
  2. Install RTools if you are on Windows
  3. Install RStudio

R and RMarkdown in RStudio (version 2023.06.1+524) was used to generate this document:

R version 4.3.1 (2023-06-16)
Platform: x86_64-apple-darwin20 (64-bit)
Running under: macOS Ventura 13.6.3

Matrix products: default
BLAS:   /Library/Frameworks/R.framework/Versions/4.3-x86_64/Resources/lib/libRblas.0.dylib 
LAPACK: /Library/Frameworks/R.framework/Versions/4.3-x86_64/Resources/lib/libRlapack.dylib;  LAPACK version 3.11.0

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

time zone: America/Toronto
tzcode source: internal

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

loaded via a namespace (and not attached):
 [1] digest_0.6.33     R6_2.5.1          fastmap_1.1.1     xfun_0.42        
 [5] cachem_1.0.8      knitr_1.43        htmltools_0.5.7   rmarkdown_2.23   
 [9] cli_3.6.1         sass_0.4.7        jquerylib_0.1.4   compiler_4.3.1   
[13] rstudioapi_0.15.0 tools_4.3.1       evaluate_0.21     bslib_0.5.0      
[17] yaml_2.3.7        rlang_1.1.1       jsonlite_1.8.7   

Libraries

Install R libraries if needed.

install.packages("rmarkdown")
install.packages("bookdown")
install.packages("knitr")
install.packages("tidyverse")
install.packages("glue")
install.packages("readxl")
install.packages("ggtext")
install.packages("scales")
install.packages("patchwork")
install.packages("DiagrammeR")
install.packages("DiagrammeRsvg")
install.packages("webshot2")
install.packages("magick")
install.packages("rsvg")
install.packages("sf")
install.packages("tmap")

Load R libraries.

library(DiagrammeR)
library(DiagrammeRsvg)
library(ggtext)
library(glue)
library(patchwork)
library(readxl)
library(rsvg)
library(sf)
library(tidyverse)
library(tmap)

Settings

settings <- list()

# Infrastructure types in order
settings$type_recode_infra <- c(
    PBL = "Cycle Track",
    BUF = "Buffered Lane",
    PL = "Painted Lane",
    LSB = "Local Street\nBikeway"
)

# Infrastructure types to remove
settings$type_filter_infra <- c("N", "None", "SR")

# Road types in order
settings$type_recode_road <- c(
    Arterial = "Arterial",
    Collector = "Collector",
    Local = "Local"
)

# Column references
settings$year_col_road <- "install_year"
settings$type_col_road <- "road_type"
settings$type_col_infra <- "infra_type"

# Set years of interest
settings$year_min <- 2009
settings$year_max <- 2022

# Plot settings
settings$line_year <- 2019
settings$basemaps <- c(
    "CartoDB.Positron",
    "CartoDB.DarkMatter",
    "Esri.WorldGrayCanvas"
)

# Apply map settings
tmap_options(basemaps = settings$basemaps)

Functions

Function 1 (calc_yearly_len): Calculate Yearly Road Length

The following function calculates yearly road lengths by infrastructure type using cumulative sums and filling in missing years and types.

For a given infrastructure type, the total road length for a given year is expressed below:

\[ length_{year,type} = f(year,type) = \sum_{i=year_{min}}^{year}l_{i, type}\ \mid\ l_{i, type} \geq 0 \]

Where:

  • \(year\) is the given year
  • \(type\) is the infrastructure type
  • \(year_{min}\) is the earliest year available in the data
  • \(l_{i,type}\) is the road length \(l\) for previous years \(i\) and infrastructure \(j\)
  • \(l_{i,type}\) is set to 0 if there is no data
#' Calculate Yearly Road Lengths By Infrastructure Type
#' 
#' Calculates the cumulative yearly road lengths by infrastructure type without considering infrastructure changes.
#'
#' @param df A data.frame with three columns containing the year, type, and road lengths.
#' @param year_col The name (char) or index (int) of the column containing the years.
#' @param type_col The name (char) or index (int) of the column containing the infrastructure type
#' @param len_col The name (char) or index (int) of the column containing the road lengths.
#' @param out_col The name (char) of the column containing the calculated yearly road lengths by type.
#'
#' @return A data.frame with three columns containing the year, type, and calculated yearly road lengths by type.
#' @export
#'
calc_yearly_len <- function(
        df,
        year_col = "install_year",
        type_col = "install_type",
        len_col = "segment_len",
        out_col = "len",
        year_min = settings$year_min,
        year_max = settings$year_max
    ) {
    
    # Convert data types
    df[[year_col]] <- as.integer(df[[year_col]])
    df[[type_col]] <- as.character(df[[type_col]])
    df[[len_col]] <- as.numeric(df[[len_col]])
    
    # Remove rows with empty type
    out <- df %>% filter(
        !is.na(.data[[type_col]])
    )
    
    # Filter to min and max years
    if (year_min > 0) {
        df <- df %>% filter(
            .data[[year_col]] >= year_min
        )
    } else {
        year_min <- min(out[[year_col]], na.rm = TRUE)
    }
    if (year_max > 0) {
        df <- df %>% filter(
            .data[[year_col]] <= year_max
        )
    } else {
        year_max <- max(out[[year_col]], na.rm = TRUE)
    }
    
    # Add dummy len for each type and year combo
    # Covers cases where type and year combo does not exist
    # E.g. No new PL installs in 2021, hence a record PL in 2021 does not exist
    type_uniq <- unique(out[[type_col]])
    type_n <- length(type_uniq)
    year_uniq <- year_min:year_max
    year_n <- length(year_uniq)
    out <- out %>% add_row(
        !!year_col := rep(year_uniq, each = type_n),
        !!type_col := rep(type_uniq, year_n),
        !!len_col := rep(0, type_n * year_n)
    )
    
    # Calc cumsum for each non-empty type ordered by year
    out <- out %>%
        arrange(.data[[year_col]]) %>%
        group_by(.data[[type_col]]) %>%
        mutate(
            !!out_col := cumsum(.data[[len_col]])
        )

    # Get the last cumsum for each year and type
    out <- out %>%
        group_by(.data[[year_col]], .data[[type_col]]) %>%
        arrange(desc(row_number())) %>%
        slice(1)
    
    # Return only the columns spec
    out <- out %>% select(c(
            year_col,
            type_col,
            out_col
        ))
    return(out)
}

Function 2 (calc_yearly_adj_len): Calculate Yearly Adjusted Road Length

The following function calculates yearly adjusted road lengths by infrastructure type using cumulative sums and filling in missing years and types.

For a given infrastructure type, the total adjusted road length for a given year is expressed below:

\[ length_{year,type}^{install} + length_{year,type}^{change_i} - length_{year,type}^{replacement_i} \] Where:

  • \(length_{year,type}^{install}\) are the yearly cumulative road lengths for an infrastructure \(type\) installation
  • \(length_{year,type}^{change_i}\) are the yearly cumulative road lengths for an infrastructure \(type\) change in order \(i\)
  • \(length_{year,type}^{replacement_i}\) are the yearly cumulative road lengths for an infrastructure \(type\) replaced by change in order \(i\)
#' Calculate Yearly Adjusted Road Lengths By Infrastructure Type
#' 
#' Calculates the cumulative yearly adjusted road lengths by infrastructure type accounting for installations and subsequent changes.
#'
#' @param df A data.frame with three columns containing the year, type, and road lengths.
#' @param year_cols A vector of the names (char) or indices (int) of the columns containing the years of installations followed by infrastructure changes in order.
#' @param type_cols A vector of the names (char) or indices (int) of the columns containing the infrastructure types of installations followed by infrastructure changes in order.
#' @param type_col The name (char) of the column containing the type.
#' @param len_cols A vector of the names (char) or indices (int) of the columns containing the road lengths of installations followed by infrastructure changes in order.
#' @param out_cols The name (char) of the column containing the calculated yearly road lengths by type.
#' @param out_col The name (char) of the column containing the calculated yearly adjusted road lengths by type.
#' @param repl_suffix A suffix (char) to append to the columns representing the road lengths of replaced infrastructure types from changes.
#' @param ... Additional arguments passed to calc_yearly_len.
#' 
#' @return A data.frame with columns containing the year, type, cumulative road lengths of installations, changes, and replacements, and calculated yearly adjusted road lengths by type.
#' @export
#'
calc_yearly_adj_len <- function(
        df,
        year_cols = c("install_year", "upgrade1_year", "upgrade2_year"),
        type_cols = c("install_type", "upgrade1_type", "upgrade2_type"),
        type_col = "type",
        len_cols = "segment_len",
        out_cols = c("install_len", "upgrade1_len", "upgrade2_len"),
        out_col = "adj_len",
        repl_suffix = "_replaced",
        ...
    ) {
    
    # Convert len_col if char
    len_cols <- rep(len_cols, length(year_cols))
    
    # Check cols same size
    year_cols_n <- length(year_cols)
    type_cols_n <- length(type_cols)
    len_cols_n <- length(len_cols)
    out_cols_n <- length(out_cols)
    if (length(unique(c(year_cols_n, type_cols_n, len_cols_n, out_cols_n))) != 1) {
        stop(glue(
            "The arguments 'year_cols' ({year_cols_n}), 'type_cols' ({type_cols_n}), 'len_cols' ({len_cols_n}), and 'out_cols' ({out_cols_n}) must be the same length."
        ))
    }
    
    # Calc yearly lens by infra type per install or change
    out <- list()
    for (i in 1:length(year_cols)) {
        
        # Get year, type, and len cols
        ycol <- year_cols[[i]]
        tcol <- type_cols[[i]]
        lcol <- len_cols[[i]]
        ocol <- out_cols[[i]]
        
        # Calc yearly len for install or change
        out <- append(
            out,
            calc_yearly_len(
                df,
                year_col = ycol,
                type_col = tcol,
                len_col = lcol,
                out_col = ocol,
                ...
            ) %>%
                rename(
                    "year" := !!ycol,
                    "type" := !!tcol
                ) %>% list
        )
        
        # Calc yearly len for replacement
        if (i > 1) {
            
            # Get repl cols
            tcol_repl <- type_cols[[i - 1]]
            lcol_repl <- len_cols[[i - 1]]
            
            # Filter for repl records only where type is not eq to change type
            df_repl <- df %>% filter(.data[[tcol]] != .data[[tcol_repl]])
            
            # Calc repl len if there are any changes
            has_change <- !is.na(df_repl[[tcol]]) %>% all 
            if (has_change) {
                out <- append(
                    out,
                    calc_yearly_len(
                        df_repl,
                        year_col = ycol,
                        type_col = tcol_repl,
                        len_col = lcol_repl,
                        out_col = glue("{ocol}{repl_suffix}"),
                        ...
                    ) %>%
                    rename(
                        "year" := !!ycol,
                        "type" := !!tcol_repl
                    ) %>% list
                )
            }
        }
    }
    
    # Combine all lens in list to single df
    out <- out %>%
        reduce(
            left_join, by = c("year", "type")
        ) %>%
        ungroup()
    
    # Create template for change and repl cols
    change_cols <- paste0(out_cols[2:out_cols_n])# change cols
    change_cols <- c(change_cols, paste0(out_cols[2:out_cols_n], repl_suffix)) # repl cols
    change_cols_add <- rep(0, length(change_cols)) # set default vals
    names(change_cols_add) <- change_cols
    
    # Add change and repl cols set to 0 if not present
    out <- out %>% add_column(
        !!!change_cols_add[setdiff(names(change_cols_add), names(.))]
    )
    
    # Set NA to 0
    out <- out %>% mutate(
        across(everything(), ~replace_na(., 0))
    )
    
    # Calc yearly adj lens by infra type
    out <- out %>%
        mutate( # added len by infra types due to install or changes
            !!out_col := reduce(across(all_of(out_cols)), `+`)
        ) %>%
        mutate( # removed len by infra types due to replacements
            !!out_col := .data[[out_col]] - reduce(
                across(all_of(
                    paste0(out_cols[2:out_cols_n], repl_suffix)
                )),
                `-`
            )
        )
    
    # Rename type col
    out <- out %>% rename(!!type_col := type)
    return(out)
}

Function 3 (plot_yearly_len): Plot Lengths by Year for Generic Types

Plots an area chart showing the cumulative road lengths by a user-defined type for each year.

This is a generic function for user-defined types such as infrastructure or road types.

#' Plot Yearly Road Lengths By Type
#' 
#' Creates an area plot of road lengths by category types.
#'
#' @param df A data.frame with three columns containing the year, type, and road lengths.
#' @param title The title (char) of the plot.
#' @param title_underline Set to TRUE to underline the title.
#' @param x_title The title (char) of the x-axis.
#' @param y_title The title (char) of the y-axis.
#' @param y_suffix The suffix (char) to add to the end of y axis values.
#' @param legend_title The title (char) of the legend.
#' @param legend Set to TRUE to include a legend.
#' @param year_col The name (char) or index (int) of the column containing the years.
#' @param year_min The minimum year (int) to display.
#' @param year_max The maximum year (int) to display.
#' @param year_int The year intervals (int) to display. For example, 1 displays every year, and 2 displays every two years.
#' @param len_col The name (char) or index (int) of the column containing the road lengths.
#' @param type_col The name (char) or index (int) of the column containing the type.
#' @param type_filter A vector (char) of types to remove fomr the plot.
#' @param type_recode A named vector (char) of names representing types and values representing the values to replace each type with.
#' @param line_50km Set to TRUE to draw the 50 km red reference line.
#' @param line_year Set to a year (int) to draw a reference line for a year. If FALSE, a line will not be drawn.
#' @param color_low The bottom color (char) of the type.
#' @param color_high The top color (char) of the type.
#' @return An area ggplot of the cumulative yearly road lengths by type.
#' @export
#'
plot_yearly_len <- function(
        df,
        title = "",
        title_underline = TRUE,
        x_title = "",
        y_title = "",
        y_suffix = " km",
        legend_title = "Type",
        legend = TRUE,
        year_col = "year",
        year_min = FALSE,
        year_max = FALSE,
        year_int = 1,
        len_col = "adj_len",
        type_col = "type",
        type_filter = c(),
        type_recode = c(),
        line_50km = FALSE,
        line_year = FALSE,
        color_low = "#DFEBF7",
        color_high = "#3683BB"
) {
    
    # Filter to start and end years
    if (year_min > 0) {
        df <- df %>% filter(
            .data[[year_col]] >= year_min
        )
    }
    if (year_max > 0) {
        df <- df %>% filter(
            .data[[year_col]] <= year_max
        )
    }
    
    # Filter out particular infrastructure types
    if (length(type_filter) > 0) {
        df <- df %>% filter(
            !.data[[type_col]] %in% type_filter
        )
    }
    
    # Recode and reorder category types
    if (length(type_recode) > 0) {
        
        # Reorder category types
        type_uniq <- unique(df[[type_col]])
        type_reorder <- names(type_recode)
        type_reorder <- c(type_reorder, type_uniq[!type_uniq %in% type_reorder])
        df[[type_col]] <- factor(df[[type_col]], levels = type_reorder)
        
        # Recode category types
        df[[type_col]] <- recode(df[[type_col]], !!!type_recode)
    }
    
    # Create fill colors
    type_n <- length(type_uniq)
    type_colors <- scales::seq_gradient_pal(
        color_low,
        color_high
    )(seq(0, 1, length.out = type_n))
    
    # Create base area plot with legend and labels
    len_max <- max(df[[len_col]], na.rm = TRUE)
    year_max <- max(df[[year_col]], na.rm = TRUE)
    out <- ggplot(
        df,
        aes(
            x = .data[[year_col]],
            y = .data[[len_col]],
            fill = .data[[type_col]],
            order = desc(.data[[type_col]])
        )
    ) +
    geom_area(colour = NA, alpha = 0.7) +
    scale_fill_manual(values = type_colors) +
    geom_line(
        position = "stack",
        size = 0.2
    ) +
    labs(
        x = x_title,
        y = y_title,
        fill = legend_title
    ) +
    guides(
        fill = FALSE,
        color = FALSE
    ) +
    scale_x_continuous(
        breaks = seq(year_min, year_max, by = year_int),
        labels = seq(year_min, year_max, by = year_int),
        limits = c(year_min, year_max)
    ) +
    scale_y_continuous(
        label = scales::label_number(suffix = y_suffix)
    ) +
    theme_minimal() +
    theme(
        plot.margin = unit(c(5,5,5,5), "points")
    )
    
    # Add title
    if (title_underline) {
        out <- out + ggtitle(
            bquote(underline(.(title)))
        )
    } else {
        out <- out + ggtitle(title)
    }
    
    # Add legend
    if (legend) {
        out <- out + guides(fill = guide_legend(
            reverse = FALSE,
            override.aes = list(
                alpha = 0.7,
                color = NA,
                shape = NA
            )
        ))
    }
    
    # Add dotted year ref line
    if (line_year) {
        out <- out + geom_vline(
            xintercept = line_year,
            color = "black",
            linetype = "dashed"
        )
    }
    
    # Add red 50km ref line
    if (line_50km) {
        out <- out + geom_segment( # 50km red line
            aes(
                x = 2009,
                y = 0,
                xend = 2009,
                yend = 50,
                color = "#bb0000",
                hjust = 0.15
            )
        ) +
        geom_segment( # 50km red triangle point down
            aes(
                x = 2009,
                y = 50.01 - (len_max * 0.05),
                xend = 2009,
                yend = 50 - (len_max * 0.05),
                color = "#bb0000",
                hjust = 0.15
            ),
            arrow = arrow(
                length = unit(0.03, "npc"),
                ends = "last",
                type = "closed"
            )
        ) +
        geom_segment( # 50km red triangle point up
            aes(
                x = 2009,
                y = (len_max * 0.05) - 0.01,
                xend = 2009,
                yend = (len_max * 0.05),
                color = "#bb0000",
                hjust = 0.15
            ),
            arrow = arrow(
                length = unit(0.03, "npc"),
                ends = "last",
                type = "closed"
            )
        ) +
        annotate(
            "text",
            x = 2009,
            y = 50,
            label = "50km",
            color = "#bb0000",
            hjust = -0.225
        )
    }
    return(out)
}

Function 4 (plot_yearly_len_infra): Plot Lengths by Year for Infrastructure Types

Plots area charts of yearly road lengths by infrastructure types for a list of data.

This uses the plot_yearly_len function.

#' Plot Yearly Road Lengths By Infrastructure Type
#' 
#' Creates area plots of road lengths by infrastructure type.
#'
#' @param df_list A list of data.frame containing the install and change years, type, and road segment lengths.
#' @return Multiple area ggplots of the cumulative yearly road lengths by infrastructure type combined with patchwork.
#' @export
#'
plot_yearly_len_infra <- function(df_list) {
    
    # Create infra plots from data
    p <- list()
    for (i in 1:length(df_list)) {
        
        # Get data and plot title
        df <- df_list[[i]]
        ptitle <- names(df_list)[[i]]
        
        # Create and add infra plot to list
        p[[i]] <- calc_yearly_adj_len(df, type_col = settings$type_col_infra) %>%
            plot_yearly_len(
                title = ptitle,
                year_min = settings$year_min,
                year_max = settings$year_max,
                type_col = settings$type_col_infra,
                type_filter = settings$type_filter_infra,
                type_recode = settings$type_recode_infra,
                legend_title = "Infrastructure Type",
                line_50km = TRUE,
                line_year = settings$line_year
            )
    }
    
    # Y-axis title
    y_title <- ggplot() +
        annotate(
            geom = "text",
            x = 1,
            y = 1,
            label = "Total Length (Centreline km)",
            angle = 90,
            size = 5
        ) +
        coord_cartesian(clip = "off")+
        theme_void()
    
    # Combine all infra plots together
    out <- (y_title | wrap_plots(p, nrow = length(p))) +
        plot_annotation(
            title = "Roadways with Dedicated Cycling Infrastructure",
            caption = sprintf("Years (%s-%s)", settings$year_min, settings$year_max),
            theme = theme(
                plot.title = element_text(hjust = 0.5, size = 16),
                plot.caption = element_text(hjust = 0.5, size = 14)
            )
        ) +
        plot_layout(widths = c(0.05, 1))
    return(out)
}

Function 5 (plot_yearly_len_road): Plot Lengths by Year for Road Types

Plots area charts of yearly road lengths by overall road type and by infrastructure separated by each road type.

This uses the plot_yearly_len function.

#' Plot Yearly Road Lengths By Road Type
#'
#' Creates area plots of road lengths by overall road type, and by infrastructure per road type.
#'
#' @param df The data.frame containing the install and change years, type, and road segment types and lengths. 
#' @return Multiple area ggplots of the cumulative yearly road lengths by road type combined with patchwork.
#' @export
#'
plot_yearly_len_road <- function(df, title = "Roadways with Dedicated Cycling Infrastructure") {
    
    # Create list to store plots
    p <- list()

    # Plot overall road types
    p[[1]] <- calc_yearly_len(
        df,
        year_col = settings$year_col_road,
        type_col = settings$type_col_road
    ) %>%
        plot_yearly_len(
            title = title,
            title_underline = FALSE,
            year_col = settings$year_col_road,
            year_min = settings$year_min,
            year_max = settings$year_max,
            x_title = sprintf("Years (%s-%s)", settings$year_min, settings$year_max),
            y_title = "Total Length (Centreline km)",
            legend_title = "Roadway Type",
            type_col = settings$type_col_road,
            type_recode = settings$type_recode_road,
            len_col = "len",
            line_50km = FALSE,
            line_year = settings$line_year,
            color_low = "#C1DDB3",
            color_high = "#297A22"
        ) +
        theme(
            plot.title = element_text(size = 18),
            plot.margin = margin(0, 0, 0, 0, "pt")
        )
    
    # Plot arterial, collector, and local road by infra
    rtypes <- c("Arterial", "Collector", "Local")
    for (i in 1:length(rtypes)) {
        
        # Get road type
        r <- rtypes[i]
        
        # Create infra plot for road type
        p[[i + 1]] <- calc_yearly_adj_len(
            df %>% filter(road_type == r),
            type_col = settings$type_col_infra
        ) %>%
            plot_yearly_len(
                title = sprintf("%s Roadways", r),
                title_underline = FALSE,
                line_50km = FALSE,
                line_year = settings$line_year,
                year_int = 2,
                x_title = sprintf("Years (%s-%s)", settings$year_min, settings$year_max),
                y_title = "Total Length (Centreline km)",
                year_min = settings$year_min,
                year_max = settings$year_max,
                type_col = settings$type_col_infra,
                type_filter = settings$type_filter_infra,
                type_recode = settings$type_recode_infra,
                legend_title = "Infrastructure Type"
            ) +
            theme(
                plot.title = element_text(size = 14),
                plot.margin = margin(0, 12, 0, 0, "pt")
            )
    }
    
    # Plot horizontal gradient bar
    grad_bar <-  ggplot(data.frame(x = 1:4), aes(x = x, y = 1, color = x)) +
        geom_line(size = 4) +
        scale_color_gradient(low = "#C1DDB3", high = "#297A22") +
        theme_void() +
        guides(color = FALSE) +
        theme(
            axis.title = element_blank(),
            axis.text = element_blank(),
            axis.ticks = element_blank(),
            axis.line = element_blank(),
            plot.margin = margin(0, 0, 0, 0, "pt")
        )
    
    # Plot overall and road type plots together
    out <- ( # overall plot
        plot_spacer() +
        p[[1]] +
        plot_spacer() +
        plot_layout(
            widths = c(0.25, 0.35, 0.2)
        )
    ) / ( # gradient bar
        plot_spacer() +
        grad_bar +
        plot_spacer() +
        plot_layout(widths = c(-0.8, 10, -1.1))
    ) / ( # infra plots
        p[[2]] +
        p[[3]] +
        p[[4]]
    ) + plot_layout(
        heights = c(12, 1, 8)
    ) + plot_annotation( # A B tags
        tag_levels = list(c("A", "", "B", "", ""))
    ) & theme(
        plot.tag = element_text(face = "bold", size = 12)
    )
    return(out)
}

Function 6 (plot_yearly_diff): Plot Yearly Differences

Plots a bar chart of differences between two columns containing years.

This function is used to check the differences in installation years between the city’s data and the verified data.

#' Plot Yearly Differences
#'
#' Creates a bar plot of the differences between two years.
#'
#' @param df The data.frame containing the two columns with the years.
#' @param year_col1 The name (char) or index (int) of the first year column.
#' @param year_col2 The name (char) or index (int) of the second year column to be subtracted from.
#' @param year_col1_name The name alias (char) of the first year column year_col1.
#' @param year_col2_name The name alias (char) of the second year column year_col2.
#' @param year_min The minimum year (int) to calculate differences for.
#' @param year_max The maximum year (int) to calculate differences for.
#' @param title The title (char) of the plot.
#' @param title_n Set to TRUE to add the number of total segments considered.
#' @param x_title The title (char) of the x-axis.
#' @param y_title The title (char) of the y-axis.
#' @param x_breaks The number (int) of breaks to show on the x-axis. Set to FALSE to let ggplot automatically decide.
#' @param x_perc Set to TRUE to show proportions and FALSE to show counts.
#' @param out_data Set to TRUE to return a list
#' 
#' @return A ggplot of yearly differences (year_col2 - year_col1), displaying the proportion of rows for each difference in years. If `out_data` is TRUE then returns a list with keys `data` representing the data used for plotting and `plot` with the ggplot object.
#' @export
#'
plot_yearly_diff <- function(
        df,
        year_col1 = "install_year_orig",
        year_col2 = "install_year",
        year_col1_name = "City Year",
        year_col2_name = "Verified Year",
        year_min = settings$year_min,
        year_max = settings$year_max,
        title = sprintf(
            "Difference in Years, Comparing %s and %s",
            year_col1_name,
            year_col2_name
        ),
        title_n = TRUE,
        x_title = sprintf(
            "Difference in Years (%s - %s)",
            year_col2_name,
            year_col1_name
        ),
        y_title = "Proportion of Total Segments",
        x_breaks = 15,
        x_perc = TRUE,
        out_data = FALSE
) {
    
    # Filter for comparable rows only
    pdata <- df %>% filter(
        !is.na(.data[[year_col1]]) & !is.na(.data[[year_col2]])
    )
    
    # Filter within min year
    if (year_min) {
        pdata <- pdata %>% filter(
            .data[[year_col1]] >= year_min | .data[[year_col2]] >= year_min
        )
    }
    
    # Filter within max year
    if (year_max) {
        pdata <- pdata %>% filter(
            .data[[year_col1]] <= year_max | .data[[year_col2]] <= year_max
        )
    }
    
    # Add n to title
    if (title_n) {
        title <- sprintf("%s (n=%s)", title, nrow(pdata))
    }
    
    # Calc yearly diff
    pdata <- pdata %>%
        mutate(year_diff = install_year - install_year_orig) %>%
        count(year_diff) %>%
        mutate(n_perc = (n / sum(n)) * 100)
    
    # Set to proportions or counts
    pdata$y <- if (x_perc) pdata$n_perc else pdata$n
    
    # Plot yealy diffs
    out <- pdata %>% 
        ggplot(aes(
            x = year_diff,
            y = y
        )) +
        geom_bar(
            stat = "identity",
            color = "#332a94",
            fill = "#c3d5e4",
            width = 1
        ) +
        labs(
            title = title,
            x = x_title,
            y = y_title
        ) +
        theme(
            plot.title = element_text(size = 12)
        )
    
    # Add percentage sign if percentages
    if (x_perc) {
        out <- out +
            scale_y_continuous(
                label = scales::label_number(suffix = "%")
            )
    }
    
    # Set x interval breaks
    if (x_breaks) {
        out <- out + scale_x_continuous(
            breaks = scales::breaks_pretty(x_breaks)
        )
    }
    
    # Returns ggplot obj or list
    if (out_data) {
        out <- list(
            data = pdata,
            plot = out
        )
    }
    return(out)
}

Data

Load raw data provided by Konrad Samsel.

Vancouver Raw Data

# Load raw data
vanc_bikeways <- read_csv("../data/vancouver_bikeways_2009_2022_v1.csv")
vanc_roads <- read_csv("../data/vancouver_roads_2009_2022_v1.csv")

# Combine raw data
vanc <- vanc_bikeways %>%
    select(
        ID_DATAENTRY,
        INST_YR_ORIG,
        INST_YR,
        INST_MIN_HTYPE,
        UPGR1_YR,
        UPGR1_MIN_HTYPE,
        UPGR2_YR,
        UPGR2_MIN_TYPE,
        ATR_SEGMENT_LENGTH
    ) %>%
    left_join(
        vanc_roads %>% select(
            ID_DATAENTRY,
            ATR_SEGMENT_TYPE
        ),
        by = "ID_DATAENTRY"
    ) %>%
    rename(
        id = ID_DATAENTRY,
        install_year_orig = INST_YR_ORIG,
        install_year = INST_YR,
        install_type = INST_MIN_HTYPE,
        upgrade1_year = UPGR1_YR,
        upgrade1_type = UPGR1_MIN_HTYPE,
        upgrade2_year = UPGR2_YR,
        upgrade2_type = UPGR2_MIN_TYPE,
        segment_len = ATR_SEGMENT_LENGTH,
        segment_type = ATR_SEGMENT_TYPE
    ) %>%
    mutate(
        segment_len = segment_len / 1000,
        road_type = case_when( # create road types
            segment_type %in% c( # arterial equiv
                "Arterial"
            ) ~ "Arterial",
            segment_type %in% c( # collector equiv
                "Collector",
                "Secondary Arterial",
                "Sec Arterial"
            ) ~ "Collector",
            segment_type %in% c( # local equiv
                "Lane",
                "Residential",
                "Leased",
                "Recreational"
            ) ~ "Local",
            .default = segment_type
        )
    )
vanc

Calgary Raw Data

# Load raw data
calg_bikeways <- read_csv("../data/calgary_bikeways_2009_2022_v1.csv")
calg_roads <- read_csv("../data/calgary_roads_2009_2022_v1.csv")

# Combine raw data
calg <- calg_bikeways %>%
    select(
        SHAPE_ID,
        YEAR_ORIG,
        INST_YR,
        INST_MIN_HTYPE,
        UPGR1_YR,
        UPGR1_MIN_HTYPE,
        UPGR2_YR,
        UPGR2_MIN_HTYPE,
        ATR_SEGMENT_LENGTH
    ) %>%
    left_join(
        calg_roads %>% select(
            shape_id,
            ctp_class
        ),
        by = join_by(SHAPE_ID == shape_id)
    ) %>%
    rename(
        id = SHAPE_ID,
        install_year_orig = YEAR_ORIG,
        install_year = INST_YR,
        install_type = INST_MIN_HTYPE,
        upgrade1_year = UPGR1_YR,
        upgrade1_type = UPGR1_MIN_HTYPE,
        upgrade2_year = UPGR2_YR,
        upgrade2_type = UPGR2_MIN_HTYPE,
        segment_len = ATR_SEGMENT_LENGTH,
        segment_type = ctp_class
    ) %>%
    mutate(
        segment_len = segment_len / 1000,
        road_type = case_when( # create road types
            segment_type %in% c( # arterial equiv
                "Arterial Street",
                "Industrial Arterial",
                "Local Arterial",
                "Parkway",
                "Urban Boulevard"
            ) ~ "Arterial",
            segment_type %in% c( # collector equiv
                "Neighbourhood Boulevard",
                "Collector",
                "Primary Collector",
                "Skeletal Road"
            ) ~ "Collector",
            segment_type %in% c( # local equiv
                "Access Route",
                "Residential Street",
                "Activity Center Street",
                "Historic Road Allowance",
                "Lanes (Alleys)",
                "Industrial Street"
            ) ~ "Local",
            .default = segment_type
        )
    )
calg

Toronto Raw Data

toron_raw <- read_sf("../data/raw/Toronto AS Export/Toronto_AS_1323.shp")

Map

tmap_mode("view")
tm_shape(toron_raw) +
    tm_lines(col = "INST_TMIN", popup.vars = TRUE)

Data

toron_raw %>% as_tibble

Details

print(toron_raw)
Simple feature collection with 1323 features and 66 fields
Geometry type: MULTILINESTRING
Dimension:     XY
Bounding box:  xmin: -79.63039 ymin: 43.58221 xmax: -79.11803 ymax: 43.85546
Geodetic CRS:  WGS 84
# A tibble: 1,323 × 67
   `_id1` OBJECTI2 SEGMENT3 INSTALL4 UPGRADE5 PRE_AMA6 STREET_7         FROM_ST8
    <dbl>    <dbl>    <dbl>    <dbl>    <dbl> <chr>    <chr>            <chr>   
 1      8        8        8     2001     2021 <NA>     Bloor St E       Parliam…
 2     17       17       17     2001     2015 <NA>     Lake Shore Blvd… Humber …
 3     18       18       18     2001     2015 <NA>     Lake Shore Blvd… 37 M E …
 4     19       19       19     2001     2015 <NA>     Lake Shore Blvd… 50.7 M …
 5     38       38       38     2001        0 <NA>     Queens Quay W    Martin …
 6     39       39       39     2001     2016 <NA>     Davenport Rd     Cotting…
 7     40       40       40     2001     2016 <NA>     Elizabeth St     College…
 8     41       41       41     2001        0 <NA>     Gerrard St E     Yonge St
 9     42       42       42     2001     2016 <NA>     Macpherson Ave   Davenpo…
10     43       43       43     2001     2016 <NA>     Lake Shore Blvd… Marine …
# ℹ 1,313 more rows
# ℹ 59 more variables: TO_STRE9 <chr>, ROADCLA10 <chr>, CNPCLAS11 <chr>,
#   SURFACE12 <chr>, OWNER13 <chr>, DIR_LOW14 <chr>, INFRA_L15 <chr>,
#   SEPA_LO16 <chr>, SEPB_LO17 <chr>, ORIG_LO18 <chr>, DIR_HIG19 <chr>,
#   INFRA_H20 <chr>, SEPA_HI21 <chr>, SEPB_HI22 <chr>, ORIG_HI23 <chr>,
#   BYLAWED24 <chr>, EDITOR25 <chr>, LAST_ED26 <chr>, UPGRADE27 <chr>,
#   CONVERT28 <chr>, OBJ2 <dbl>, ID_SEAN <chr>, C_INST_YR <dbl>, …

Preprocessing

Toronto Preprocessed Data

Note 1: Working on getting complete data.

# Preprocess data
toron_prep <- toron_raw %>%
    select( # select and rename
        id = OBJECTI2,
        street = STREET_7,
        street_from = FROM_ST8,
        street_to = TO_STRE9,
        install_year = INSTALL4,
        install_type = INFRA_H20, # similar to C_INFRA_H
        verify_install_year = INST_YR,
        verify_install_date = INST_DATE,
        verify_install_type = INST_TMIN,
        verify_install_comment = INST_COMM,
        verify_upgrade1_year = UPGR1_YR,
        verify_upgrade1_date = UPGR1_DATE,
        verify_upgrade1_type = UPGR1_TMIN,
        verify_upgrade1_comment = UPGR1_COMM,
        verify_upgrade2_year = UPGR2_YR,
        verify_upgrade2_date = UPGR2_DATE,
        verify_upgrade2_type = UPGR2_TMIN,
        verify_upgrade2_comment = UPGR2_COMM,
        verify_excl_flag = EXCL_FLAG
    ) %>%
    mutate( # data types
        id = as.character(id),
        street = as.character(street),
        street_from = as.character(street_from),
        street_to = as.character(street_to),
        install_year = as.numeric(install_year),
        install_type = as.character(install_type),
        verify_install_year = as.numeric(verify_install_year),
        verify_install_date = as.character(verify_install_date),
        verify_install_type = as.character(verify_install_type),
        verify_install_comment = as.character(verify_install_comment),
        verify_upgrade1_year = as.numeric(verify_upgrade1_year),
        verify_upgrade1_date = as.character(verify_upgrade1_date),
        verify_upgrade1_type = as.character(verify_upgrade1_type),
        verify_upgrade1_comment = as.character(verify_upgrade1_comment),
        verify_upgrade2_year = as.numeric(verify_upgrade2_year),
        verify_upgrade2_date = as.character(verify_upgrade2_date),
        verify_upgrade2_type = as.character(verify_upgrade2_type),
        verify_upgrade2_comment = as.character(verify_upgrade2_comment),
        verify_excl_flag = as.character(verify_excl_flag)
    ) %>%
    mutate( # clean values
        install_year = na_if(install_year, 0),
        verify_install_year = na_if(verify_install_year, 0),
        verify_install_date = na_if(verify_install_date, "NA"),
        verify_install_type = na_if(verify_install_type, "NA") %>%
            str_replace_all("[^[:alpha:]]|\\s", ""),
        verify_install_comment = na_if(verify_install_comment, "NA"),
        verify_upgrade1_year = na_if(verify_upgrade1_year, 0),
        verify_upgrade1_date = na_if(verify_upgrade1_date, "NA"),
        verify_upgrade1_type = na_if(verify_upgrade1_type, "NA") %>%
            str_replace_all("[^[:alpha:]]|\\s", ""),
        verify_upgrade1_comment = na_if(verify_upgrade1_comment, "NA"),
        verify_upgrade2_year = na_if(verify_upgrade2_year, 0),
        verify_upgrade2_date = na_if(verify_upgrade2_date, "NA"),
        verify_upgrade2_type = na_if(verify_upgrade2_type, "NA") %>%
            str_replace_all("[^[:alpha:]]|\\s", ""),
        verify_upgrade2_comment = na_if(verify_upgrade2_comment, "NA"),
        verify_excl_flag =  na_if(verify_excl_flag, "NA") %>%
            str_replace_all("\\s", "")
    ) %>%
    mutate( # create columns
        len_km = as.numeric(st_length(geometry)) / 1000,
        .before = geometry
    )

# Save geojson
toron_prep %>%
    st_write("../data/toronto_bikeways_v1.geojson", delete_dsn = TRUE)

# Save csv
# st_read("../data/toronto_bikeways_v1.csv", options = "GEOM_POSSIBLE_NAMES=geometry", crs = "urn:ogc:def:crs:OGC:1.3:CRS84")
toron_prep %>%
    mutate(
        geometry = st_as_text(geometry),
        geometry_crs = st_crs(toron_prep)$proj4string,
        .before = geometry
    ) %>%
    write_csv("../data/toronto_bikeways_v1.csv", na = "")
toron_prep

Note 2: Using this one for now.

# Load raw data
toron_bikeways <- read_csv("../data/toronto_bikeways_2009_2022_v1.csv")
toron_roads <- read_csv("../data/toronto_roads_2009_2022_v1.csv")

# Combine raw data
toron <- toron_bikeways %>%
    select(
        ID_OID,
        INSTALLED_ORIG,
        INST_YR,
        INST_MIN_HTYPE,
        UPGR1_YR,
        UPGR1_MIN_HTYPE,
        UPGR2_YR,
        UPGR2_MIN_HTYPE,
        ATR_SEGMENT_LENGTH
    ) %>%
    left_join(
        toron_roads %>% select(
            OID_,
            FEATURE36
        ),
        by = join_by(ID_OID == OID_)
    ) %>%
    rename(
        id = ID_OID,
        install_year_orig = INSTALLED_ORIG,
        install_year = INST_YR,
        install_type = INST_MIN_HTYPE,
        upgrade1_year = UPGR1_YR,
        upgrade1_type = UPGR1_MIN_HTYPE,
        upgrade2_year = UPGR2_YR,
        upgrade2_type = UPGR2_MIN_HTYPE,
        segment_len = ATR_SEGMENT_LENGTH,
        segment_type = FEATURE36
    ) %>%
    mutate(
        segment_len = segment_len / 1000,
        road_type = case_when( # create road types
            segment_type %in% c( # arterial equiv
                "Major Arterial",
                "Major Arterial Ramp",
                "Minor Arterial"
            ) ~ "Arterial",
            segment_type %in% c( # collector equiv
                "Collector"
            ) ~ "Collector",
            segment_type %in% c(  # local equiv
                "Local",
                "Other"
            ) ~ "Local",
            .default = segment_type
        )
    )
toron

Figures

Figure 1: Flow diagram of inclusion criteria for bikeway segments in Vancouver, Calgary, and Toronto.

This flowchart provides a high-level overview of the segment inclusions and exclusions for each municipality. Data from Calgary were specific to on-street routes only. For detailed flow diagrams specific to each municipality, please refer to the Appendix.

grViz("
digraph {
    rankdir = LR
    node[
        shape = box,
        width = 2.75,
        height = 1.35,
        style = filled,
        fillcolor = white,
        penwidth = 1.5,
        fontname = 'Arial'
    ]
    layout = neato
    
    filter1[
        label = 'Filtering',
        height = 0.25,
        shape = plaintext,
        style='', pos = '1.6,1.425!'
    ]
    filter2[
        style = invis,
        pos = '1.6,-3.9!'
    ]
    filter1 -> filter2 [style = dashed, dir = none, color = '#b0b0b0']
    
    screen1[
        label = 'Screening',
        height = 0.25,
        shape = plaintext,
        style='', pos = '4.85,1.425!'
    ]
    screen2[
        style = invis,
        pos = '4.85,-3.9!'
    ]
    screen1 -> screen2 [style = dashed, dir = none, color = '#b0b0b0']
    
    open_data[
        label = 'Open Data',
        height = 0.5,
        fillcolor = '#d7e9fe',
        pos = '0,1!'
    ]
    elig_data[
        label = 'Eligible Segments',
        height = 0.5,
        fillcolor = '#d7e9fe',
        pos = '3.25,1!'
    ]
    incl_data[
        label = 'Inclusions',
        height = 0.5,
        fillcolor = '#d7e9fe',
        pos = '6.5,1!'
    ]
    
    open_vanc[
        label = <<b>Vancouver</b><br/>N = @@1-1 Segments<br/><i>(@@2-1)<br/>Downloaded: January 2023</i>>,
        pos = '0,-0.06!'
    ]
    open_calg[
        label = <<b>Calgary</b><br/>N = @@1-2 Segments<br/><i>(@@2-2)<br/>Downloaded: January 2023</i>>,
        pos = '0,-1.65!'
    ]
    open_toron[
        label = <<b>Toronto</b><br/>N = @@1-3 Segments<br/><i>(@@2-3)<br/>Downloaded: January 2023</i>>, 
        pos = '0,-3.24!'
    ]
    
    elig_vanc[
        label = <n = @@3-1 Segments<br/>(@@4-1)<br/><i><b>Exclusions</b>@@5-1@@6-1@@7-1</i>>,
        pos = '3.25,-0.06!'
    ]
    
    elig_calg[
        label = <n = @@3-2 Segments<br/><i>(@@4-2)<br/><b>Exclusions</b>@@5-2@@6-2@@7-2</i>>,
        pos = '3.25,-1.65!'
    ]
    elig_toron[
        label = <n = @@3-3 Segments<br/><i>(@@4-3)<br/><b>Exclusions</b>@@5-3@@6-3@@7-3</i>>,
        pos = '3.25,-3.24!'
    ]
    
    incl_vanc[
        label = <n = @@8-1 Segments<br/><i>(@@9-1)<br/><b>Exclusions</b>@@10-1@@11-1</i>>,
        pos = '6.5,-0.06!'
    ]
    incl_calg[
        label = <n = @@8-2 Segments<br/><i>(@@9-2)<br/><b>Exclusions</b>@@10-2@@11-2</i>>,
        pos = '6.5,-1.65!'
    ]
    incl_toron[
        label = <n = @@8-3 Segments<br/><i>(@@9-3)<br/><b>Exclusions</b>@@10-3@@11-3</i>>,
        pos = '6.5,-3.24!'
    ]
    
    note[
        label=<<i>@@12</i>>,
        style = '',
        shape = plaintext,
        fontsize = 12,
        pos = '3.25,-4.15!'
    ]
    
    open_vanc -> elig_vanc -> incl_vanc
    open_calg -> elig_calg -> incl_calg
    open_toron -> elig_toron -> incl_toron
}

[1]: c('3664', '4166', '1323') # open segments
[2]: c('342.1 km', '569.7 km', '755.8 km') # open km
[3]: c('780', '782', '331') # eligible segments
[4]: c('71.2 km', '88.7 km', '205.4 km') # eligible km
[5]: c('<br/>Ineligible: n=2883', '<br/>Ineligible: n=3383', '<br/>Ineligible: n=992') # eligible exclusions
[6]: c('<br/>Duplicates n=1', '', '') # eligble duplicates
[7]: c('', '<br/>No Polyline Data: n=1', '') # eligible polyline data
[8]: c('745', '750', '326') # inclusion segments
[9]: c('69.9 km', '85 km', '203.5 km') # inclusion km
[10]: c('<br/>*Misclassifications: n=34', '<br/>*Misclassifications: n=32', '<br/>*Misclassifications: n=5') # inclusion misclassifications
[11]: c('<br/>Duplicates: n=1', '', '') # inclusion duplicates
[12]: '*Denotes segments misclassified as an ineligible type (off-street path, shared road, or inactive temporary infrastructure)'
")
%>%
    export_svg %>%
    charToRaw %>%
    rsvg_png("test.png")

Figure 2: Changes in dedicated cycling infrastructure between 2009 and 2022 for Vancouver, Calgary, and Toronto by infrastructure category.

Assessed using roadway centreline-km, with infrastructure classifications determined by the most protective element present along each road segment.

plot_yearly_len_infra(list(
    "Vancouver, CA" = vanc,
    "Calgary, CA" = calg,
    "Toronto, CA" = toron
))

Figure 4: Changes in Dedicated On-Street Infrastructure Since January 2020 for Vancouver, Calgary, and Toronto.

New installations of dedicated infrastructure are denoted in green, upgrades from a previous dedicated infrastructure type are denoted in orange. Mapped in ArcGIS Pro 3.0.1.

Appendix 1 - Supplementary Results

Supplementary Figure 4: Changes in dedicated cycling infrastructure between 2009 and 2021 for the Municipality of Vancouver, CA.

By (A) roadway classification, and (B) infrastructure distribution within each road class. Assessed using roadway centreline-km, with infrastructure classification determined by the most protective element present along each road segment.

plot_yearly_len_road(
    vanc,
    title = "Roadways with Dedicated Cycling Infrastructure (Vancouver, CA)"
)

Supplementary Figure 5: Changes in dedicated cycling infrastructure between 2009 and 2022 for the Municipality of Calgary, CA.

By (A) roadway classification, and (B) infrastructure distribution within each road class. Assessed using roadway centreline-km, with infrastructure classification determined by the most protective element present along each road segment.

plot_yearly_len_road(
    calg,
    title = "Roadways with Dedicated Cycling Infrastructure (Calgary, CA)"
)

Supplementary Figure 6: Changes in dedicated cycling infrastructure between 2009 and 2022 for the Municipality of Toronto, CA.

By (A) roadway classification, and (B) infrastructure distribution within each road class. Assessed using roadway centreline-km, with infrastructure classification determined by the most protective element present along each road segment.

plot_yearly_len_road(
    toron,
    title = "Roadways with Dedicated Cycling Infrastructure (Toronto, CA)"
)

Supplementary Figure 7: A comparative analysis between municipal data and verified data on the installation years for cycling infrastructure in Vancouver, CA.

# Create the plot
sfig7 <- plot_yearly_diff(
    vanc,
    title = "Difference in Installation Years, Comparing City Data and Verified Data: Vancouver, CA",
    out_data = TRUE
)

# Calc metrics for description
sfig7_n <- sum(sfig7$data$n)
sfig7_0 <- sfig7$data %>%  # perc correct
    filter(year_diff == 0) %>%
    pull(n_perc) %>%
    round(1)
sfig7_pm1 <- sfig7$data %>% # perc plus/minus 1
    filter(year_diff >= -1 & year_diff <= 1) %>%
    pull(n_perc) %>%
    sum %>%
    round(1)

Any data where a city provided and verified installation years were missing or the verified year occurred earlier than the start of the study period (2009) has been excluded from analysis, yielding n=393 segments. The graph shows that 83% of the included segments had the correct installation year as per the city’s data, and 92.4% were accurate within a range of ±1 year.

Plot

sfig7$plot

Data

sfig7$data

Supplementary Figure 8: A comparative analysis between municipal data and verified data on the installation years for cycling infrastructure in Calgary, CA.

# Create the plot
sfig8 <- plot_yearly_diff(
    calg,
    title = "Difference in Installation Years, Comparing City Data and Verified Data: Calgary, CA",
    out_data = TRUE
)

# Calc metrics for description
sfig8_n <- sum(sfig8$data$n)
sfig8_0 <- sfig8$data %>%  # perc correct
    filter(year_diff == 0) %>%
    pull(n_perc) %>%
    round(1)
sfig8_pm1 <- sfig8$data %>% # perc plus/minus 1
    filter(year_diff >= -1 & year_diff <= 1) %>%
    pull(n_perc) %>%
    sum %>%
    round(1)

Any data where a city provided and verified installation years were missing or the verified year occurred earlier than the start of the study period (2009) has been excluded from analysis, yielding n=709 segments. The graph shows that 39.8% of the included segments had the correct installation year as per the city’s data, and 59.9% were accurate within a range of ±1 year.

Plot

sfig8$plot

Data

sfig8$data

Supplementary Figure 9: A comparative analysis between municipal data and verified data on the installation years for cycling infrastructure in Toronto, CA.

# Create the plot
sfig9 <- plot_yearly_diff(
    toron,
    title = "Difference in Installation Years, Comparing City Data and Verified Data: Toronto, CA",
    out_data = TRUE
)

# Calc metrics for description
sfig9_n <- sum(sfig9$data$n)
sfig9_0 <- sfig9$data %>%  # perc correct
    filter(year_diff == 0) %>%
    pull(n_perc) %>%
    round(1)
sfig9_pm1 <- sfig9$data %>% # perc plus/minus 1
    filter(year_diff >= -1 & year_diff <= 1) %>%
    pull(n_perc) %>%
    sum %>%
    round(1)

Any data where a city provided and verified installation years were missing or the verified year occurred earlier than the start of the study period (2009) has been excluded from analysis, yielding n=176 segments. The graph shows that 41.5% of the included segments had the correct installation year as per the city’s data, and 43.2% were accurate within a range of ±1 year.

Plot

sfig9$plot

Data

sfig9$data

Contributions

Richard Wen developed reproducible R code and organized the data based on Konrad Samsel’s draft manuscript and previous R code. Konrad Samsel prepared draft manuscript, raw data, and provided consultation on data and methods.

Acknowledgements

Linda Rothman and Brice Batomen provided supervision, project administration, resources, funding, and review/editing for the draft manuscript.

LS0tCnRpdGxlOiAiUGVkYWxsaW5nIEZvcndhcmQ6IFRoZSBFdm9sdXRpb24gb2YgRGVkaWNhdGVkIEN5Y2xpbmcgSW5mcmFzdHJ1Y3R1cmUgaW4gQ2FuYWRpYW4gQ2l0aWVzIGZyb20gMjAxMCB0byAyMDIyIgpzdWJ0aXRsZTogIlIgQ29kZSBmb3IgRmlndXJlcyBhbmQgVGFibGVzIgphdXRob3I6Ci0gIlJpY2hhcmQgV2VuIHJpY2hhcmQud2VuQHV0b3JvbnRvLmNhIgotICJLb25yYWQgU2Ftc2VsIGtvbnJhZC5zYW1zZWxAbWFpbC51dG9yb250by5jYSIKLSAiQnJpY2UgQmF0b21lbiBicmljZS5rdWltaUB1dG9yb250by5jYSIKLSAiTGluZGEgUm90aG1hbiBsaW5kYS5yb3RobWFuQHRvcm9udG9tdS5jYSIKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJUIgJWQsICVZJylgIgprbml0OiB8CiAgICAoZnVuY3Rpb24oaW5wdXRfcm1kLCAuLi4pIHsKICAgIHJtYXJrZG93bjo6cmVuZGVyKAogICAgICAgIGlucHV0X3JtZCwKICAgICAgICBybWFya2Rvd246Omh0bWxfbm90ZWJvb2soCiAgICAgICAgICAgIHRvYyA9IFRSVUUsCiAgICAgICAgICAgIHRvY19mbG9hdCA9IFRSVUUsCiAgICAgICAgICAgIGhpZ2hsaWdodCA9ICJ6ZW5idXJuIiwKICAgICAgICAgICAgY29kZV9mb2xkaW5nID0gImhpZGUiCiAgICAgICAgKSwKICAgICAgICBvdXRwdXRfZGlyID0gIi4uL2RvY3MiLAogICAgICAgIG91dHB1dF9maWxlID0gImluZGV4IiwgLi4uKQogICAgcmVuYW1lZCA8LSBmaWxlLnJlbmFtZSgiLi4vZG9jcy9pbmRleC5uYi5odG1sIiwgIi4uL2RvY3MvaW5kZXguaHRtbCIpCiAgICB9KQotLS0KCmBgYHtyIGluY2x1ZGUgPSBGQUxTRSwgZWNobyA9IEZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoCgl3YXJuaW5nID0gRkFMU0UsCgltZXNzYWdlID0gRkFMU0UKKQpgYGAKCiMgSW5zdGFsbGF0aW9uCgoxLiBJbnN0YWxsIFtSXShodHRwczovL3d3dy5yLXByb2plY3Qub3JnLykKMi4gSW5zdGFsbCBbUlRvb2xzXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy8pIGlmIHlvdSBhcmUgb24gV2luZG93cwozLiBJbnN0YWxsIFtSU3R1ZGlvXShodHRwczovL3Bvc2l0LmNvL2Rvd25sb2FkL3JzdHVkaW8tZGVza3RvcC8pCgpSIGFuZCBSTWFya2Rvd24gaW4gUlN0dWRpbyAodmVyc2lvbiAyMDIzLjA2LjErNTI0KSB3YXMgdXNlZCB0byBnZW5lcmF0ZSB0aGlzIGRvY3VtZW50OgoKYGBge3IgZWNobz1GQUxTRX0Kc2Vzc2lvbkluZm8oKQpgYGAKCiMgTGlicmFyaWVzCgpJbnN0YWxsIFIgbGlicmFyaWVzIGlmIG5lZWRlZC4KCmBgYHtyIGV2YWw9RkFMU0V9Cmluc3RhbGwucGFja2FnZXMoInJtYXJrZG93biIpCmluc3RhbGwucGFja2FnZXMoImJvb2tkb3duIikKaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQppbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQppbnN0YWxsLnBhY2thZ2VzKCJnbHVlIikKaW5zdGFsbC5wYWNrYWdlcygicmVhZHhsIikKaW5zdGFsbC5wYWNrYWdlcygiZ2d0ZXh0IikKaW5zdGFsbC5wYWNrYWdlcygic2NhbGVzIikKaW5zdGFsbC5wYWNrYWdlcygicGF0Y2h3b3JrIikKaW5zdGFsbC5wYWNrYWdlcygiRGlhZ3JhbW1lUiIpCmluc3RhbGwucGFja2FnZXMoIkRpYWdyYW1tZVJzdmciKQppbnN0YWxsLnBhY2thZ2VzKCJ3ZWJzaG90MiIpCmluc3RhbGwucGFja2FnZXMoIm1hZ2ljayIpCmluc3RhbGwucGFja2FnZXMoInJzdmciKQppbnN0YWxsLnBhY2thZ2VzKCJzZiIpCmluc3RhbGwucGFja2FnZXMoInRtYXAiKQpgYGAKCkxvYWQgUiBsaWJyYXJpZXMuCgpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KERpYWdyYW1tZVIpCmxpYnJhcnkoRGlhZ3JhbW1lUnN2ZykKbGlicmFyeShnZ3RleHQpCmxpYnJhcnkoZ2x1ZSkKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkocmVhZHhsKQpsaWJyYXJ5KHJzdmcpCmxpYnJhcnkoc2YpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHRtYXApCmBgYAoKIyBTZXR0aW5ncwoKYGBge3J9CnNldHRpbmdzIDwtIGxpc3QoKQoKIyBJbmZyYXN0cnVjdHVyZSB0eXBlcyBpbiBvcmRlcgpzZXR0aW5ncyR0eXBlX3JlY29kZV9pbmZyYSA8LSBjKAoJUEJMID0gIkN5Y2xlIFRyYWNrIiwKCUJVRiA9ICJCdWZmZXJlZCBMYW5lIiwKCVBMID0gIlBhaW50ZWQgTGFuZSIsCglMU0IgPSAiTG9jYWwgU3RyZWV0XG5CaWtld2F5IgopCgojIEluZnJhc3RydWN0dXJlIHR5cGVzIHRvIHJlbW92ZQpzZXR0aW5ncyR0eXBlX2ZpbHRlcl9pbmZyYSA8LSBjKCJOIiwgIk5vbmUiLCAiU1IiKQoKIyBSb2FkIHR5cGVzIGluIG9yZGVyCnNldHRpbmdzJHR5cGVfcmVjb2RlX3JvYWQgPC0gYygKCUFydGVyaWFsID0gIkFydGVyaWFsIiwKCUNvbGxlY3RvciA9ICJDb2xsZWN0b3IiLAoJTG9jYWwgPSAiTG9jYWwiCikKCiMgQ29sdW1uIHJlZmVyZW5jZXMKc2V0dGluZ3MkeWVhcl9jb2xfcm9hZCA8LSAiaW5zdGFsbF95ZWFyIgpzZXR0aW5ncyR0eXBlX2NvbF9yb2FkIDwtICJyb2FkX3R5cGUiCnNldHRpbmdzJHR5cGVfY29sX2luZnJhIDwtICJpbmZyYV90eXBlIgoKIyBTZXQgeWVhcnMgb2YgaW50ZXJlc3QKc2V0dGluZ3MkeWVhcl9taW4gPC0gMjAwOQpzZXR0aW5ncyR5ZWFyX21heCA8LSAyMDIyCgojIFBsb3Qgc2V0dGluZ3MKc2V0dGluZ3MkbGluZV95ZWFyIDwtIDIwMTkKc2V0dGluZ3MkYmFzZW1hcHMgPC0gYygKCSJDYXJ0b0RCLlBvc2l0cm9uIiwKCSJDYXJ0b0RCLkRhcmtNYXR0ZXIiLAoJIkVzcmkuV29ybGRHcmF5Q2FudmFzIgopCgojIEFwcGx5IG1hcCBzZXR0aW5ncwp0bWFwX29wdGlvbnMoYmFzZW1hcHMgPSBzZXR0aW5ncyRiYXNlbWFwcykKYGBgCgojIEZ1bmN0aW9ucwoKIyMgRnVuY3Rpb24gMSAoY2FsY195ZWFybHlfbGVuKTogQ2FsY3VsYXRlIFllYXJseSBSb2FkIExlbmd0aAoKVGhlIGZvbGxvd2luZyBmdW5jdGlvbiBjYWxjdWxhdGVzIHllYXJseSByb2FkIGxlbmd0aHMgYnkgaW5mcmFzdHJ1Y3R1cmUgdHlwZSB1c2luZyBjdW11bGF0aXZlIHN1bXMgYW5kIGZpbGxpbmcgaW4gbWlzc2luZyB5ZWFycyBhbmQgdHlwZXMuCgpGb3IgYSBnaXZlbiBpbmZyYXN0cnVjdHVyZSB0eXBlLCB0aGUgdG90YWwgcm9hZCBsZW5ndGggZm9yIGEgZ2l2ZW4geWVhciBpcyBleHByZXNzZWQgYmVsb3c6CgokJApsZW5ndGhfe3llYXIsdHlwZX0gPSBmKHllYXIsdHlwZSkgPSBcc3VtX3tpPXllYXJfe21pbn19Xnt5ZWFyfWxfe2ksIHR5cGV9XCBcbWlkXCBsX3tpLCB0eXBlfSBcZ2VxIDAKJCQKCldoZXJlOgoKLSAgICR5ZWFyJCBpcyB0aGUgZ2l2ZW4geWVhcgotICAgJHR5cGUkIGlzIHRoZSBpbmZyYXN0cnVjdHVyZSB0eXBlCi0gICAkeWVhcl97bWlufSQgaXMgdGhlIGVhcmxpZXN0IHllYXIgYXZhaWxhYmxlIGluIHRoZSBkYXRhCi0gICAkbF97aSx0eXBlfSQgaXMgdGhlIHJvYWQgbGVuZ3RoICRsJCBmb3IgcHJldmlvdXMgeWVhcnMgJGkkIGFuZCBpbmZyYXN0cnVjdHVyZSAkaiQKLSAgICRsX3tpLHR5cGV9JCBpcyBzZXQgdG8gMCBpZiB0aGVyZSBpcyBubyBkYXRhCgpgYGB7cn0KCiMnIENhbGN1bGF0ZSBZZWFybHkgUm9hZCBMZW5ndGhzIEJ5IEluZnJhc3RydWN0dXJlIFR5cGUKIycgCiMnIENhbGN1bGF0ZXMgdGhlIGN1bXVsYXRpdmUgeWVhcmx5IHJvYWQgbGVuZ3RocyBieSBpbmZyYXN0cnVjdHVyZSB0eXBlIHdpdGhvdXQgY29uc2lkZXJpbmcgaW5mcmFzdHJ1Y3R1cmUgY2hhbmdlcy4KIycKIycgQHBhcmFtIGRmIEEgZGF0YS5mcmFtZSB3aXRoIHRocmVlIGNvbHVtbnMgY29udGFpbmluZyB0aGUgeWVhciwgdHlwZSwgYW5kIHJvYWQgbGVuZ3Rocy4KIycgQHBhcmFtIHllYXJfY29sIFRoZSBuYW1lIChjaGFyKSBvciBpbmRleCAoaW50KSBvZiB0aGUgY29sdW1uIGNvbnRhaW5pbmcgdGhlIHllYXJzLgojJyBAcGFyYW0gdHlwZV9jb2wgVGhlIG5hbWUgKGNoYXIpIG9yIGluZGV4IChpbnQpIG9mIHRoZSBjb2x1bW4gY29udGFpbmluZyB0aGUgaW5mcmFzdHJ1Y3R1cmUgdHlwZQojJyBAcGFyYW0gbGVuX2NvbCBUaGUgbmFtZSAoY2hhcikgb3IgaW5kZXggKGludCkgb2YgdGhlIGNvbHVtbiBjb250YWluaW5nIHRoZSByb2FkIGxlbmd0aHMuCiMnIEBwYXJhbSBvdXRfY29sIFRoZSBuYW1lIChjaGFyKSBvZiB0aGUgY29sdW1uIGNvbnRhaW5pbmcgdGhlIGNhbGN1bGF0ZWQgeWVhcmx5IHJvYWQgbGVuZ3RocyBieSB0eXBlLgojJwojJyBAcmV0dXJuIEEgZGF0YS5mcmFtZSB3aXRoIHRocmVlIGNvbHVtbnMgY29udGFpbmluZyB0aGUgeWVhciwgdHlwZSwgYW5kIGNhbGN1bGF0ZWQgeWVhcmx5IHJvYWQgbGVuZ3RocyBieSB0eXBlLgojJyBAZXhwb3J0CiMnCmNhbGNfeWVhcmx5X2xlbiA8LSBmdW5jdGlvbigKCQlkZiwKCQl5ZWFyX2NvbCA9ICJpbnN0YWxsX3llYXIiLAoJCXR5cGVfY29sID0gImluc3RhbGxfdHlwZSIsCgkJbGVuX2NvbCA9ICJzZWdtZW50X2xlbiIsCgkJb3V0X2NvbCA9ICJsZW4iLAoJCXllYXJfbWluID0gc2V0dGluZ3MkeWVhcl9taW4sCgkJeWVhcl9tYXggPSBzZXR0aW5ncyR5ZWFyX21heAoJKSB7CgkKCSMgQ29udmVydCBkYXRhIHR5cGVzCglkZltbeWVhcl9jb2xdXSA8LSBhcy5pbnRlZ2VyKGRmW1t5ZWFyX2NvbF1dKQoJZGZbW3R5cGVfY29sXV0gPC0gYXMuY2hhcmFjdGVyKGRmW1t0eXBlX2NvbF1dKQoJZGZbW2xlbl9jb2xdXSA8LSBhcy5udW1lcmljKGRmW1tsZW5fY29sXV0pCgkKCSMgUmVtb3ZlIHJvd3Mgd2l0aCBlbXB0eSB0eXBlCglvdXQgPC0gZGYgJT4lIGZpbHRlcigKCQkhaXMubmEoLmRhdGFbW3R5cGVfY29sXV0pCgkpCgkKCSMgRmlsdGVyIHRvIG1pbiBhbmQgbWF4IHllYXJzCglpZiAoeWVhcl9taW4gPiAwKSB7CgkJZGYgPC0gZGYgJT4lIGZpbHRlcigKCQkJLmRhdGFbW3llYXJfY29sXV0gPj0geWVhcl9taW4KCQkpCgl9IGVsc2UgewoJCXllYXJfbWluIDwtIG1pbihvdXRbW3llYXJfY29sXV0sIG5hLnJtID0gVFJVRSkKCX0KCWlmICh5ZWFyX21heCA+IDApIHsKCQlkZiA8LSBkZiAlPiUgZmlsdGVyKAoJCQkuZGF0YVtbeWVhcl9jb2xdXSA8PSB5ZWFyX21heAoJCSkKCX0gZWxzZSB7CgkJeWVhcl9tYXggPC0gbWF4KG91dFtbeWVhcl9jb2xdXSwgbmEucm0gPSBUUlVFKQoJfQoJCgkjIEFkZCBkdW1teSBsZW4gZm9yIGVhY2ggdHlwZSBhbmQgeWVhciBjb21ibwoJIyBDb3ZlcnMgY2FzZXMgd2hlcmUgdHlwZSBhbmQgeWVhciBjb21ibyBkb2VzIG5vdCBleGlzdAoJIyBFLmcuIE5vIG5ldyBQTCBpbnN0YWxscyBpbiAyMDIxLCBoZW5jZSBhIHJlY29yZCBQTCBpbiAyMDIxIGRvZXMgbm90IGV4aXN0Cgl0eXBlX3VuaXEgPC0gdW5pcXVlKG91dFtbdHlwZV9jb2xdXSkKCXR5cGVfbiA8LSBsZW5ndGgodHlwZV91bmlxKQoJeWVhcl91bmlxIDwtIHllYXJfbWluOnllYXJfbWF4Cgl5ZWFyX24gPC0gbGVuZ3RoKHllYXJfdW5pcSkKCW91dCA8LSBvdXQgJT4lIGFkZF9yb3coCgkJISF5ZWFyX2NvbCA6PSByZXAoeWVhcl91bmlxLCBlYWNoID0gdHlwZV9uKSwKCQkhIXR5cGVfY29sIDo9IHJlcCh0eXBlX3VuaXEsIHllYXJfbiksCgkJISFsZW5fY29sIDo9IHJlcCgwLCB0eXBlX24gKiB5ZWFyX24pCgkpCgkKCSMgQ2FsYyBjdW1zdW0gZm9yIGVhY2ggbm9uLWVtcHR5IHR5cGUgb3JkZXJlZCBieSB5ZWFyCglvdXQgPC0gb3V0ICU+JQoJCWFycmFuZ2UoLmRhdGFbW3llYXJfY29sXV0pICU+JQoJCWdyb3VwX2J5KC5kYXRhW1t0eXBlX2NvbF1dKSAlPiUKCQltdXRhdGUoCgkJCSEhb3V0X2NvbCA6PSBjdW1zdW0oLmRhdGFbW2xlbl9jb2xdXSkKCQkpCgoJIyBHZXQgdGhlIGxhc3QgY3Vtc3VtIGZvciBlYWNoIHllYXIgYW5kIHR5cGUKCW91dCA8LSBvdXQgJT4lCgkJZ3JvdXBfYnkoLmRhdGFbW3llYXJfY29sXV0sIC5kYXRhW1t0eXBlX2NvbF1dKSAlPiUKCQlhcnJhbmdlKGRlc2Mocm93X251bWJlcigpKSkgJT4lCgkJc2xpY2UoMSkKCQoJIyBSZXR1cm4gb25seSB0aGUgY29sdW1ucyBzcGVjCglvdXQgPC0gb3V0ICU+JSBzZWxlY3QoYygKCQkJeWVhcl9jb2wsCgkJCXR5cGVfY29sLAoJCQlvdXRfY29sCgkJKSkKCXJldHVybihvdXQpCn0KYGBgCgojIyBGdW5jdGlvbiAyIChjYWxjX3llYXJseV9hZGpfbGVuKTogQ2FsY3VsYXRlIFllYXJseSBBZGp1c3RlZCBSb2FkIExlbmd0aAoKVGhlIGZvbGxvd2luZyBmdW5jdGlvbiBjYWxjdWxhdGVzIHllYXJseSBhZGp1c3RlZCByb2FkIGxlbmd0aHMgYnkgaW5mcmFzdHJ1Y3R1cmUgdHlwZSB1c2luZyBjdW11bGF0aXZlIHN1bXMgYW5kIGZpbGxpbmcgaW4gbWlzc2luZyB5ZWFycyBhbmQgdHlwZXMuCgpGb3IgYSBnaXZlbiBpbmZyYXN0cnVjdHVyZSB0eXBlLCB0aGUgdG90YWwgYWRqdXN0ZWQgcm9hZCBsZW5ndGggZm9yIGEgZ2l2ZW4geWVhciBpcyBleHByZXNzZWQgYmVsb3c6CgokJApsZW5ndGhfe3llYXIsdHlwZX1ee2luc3RhbGx9ICsgbGVuZ3RoX3t5ZWFyLHR5cGV9XntjaGFuZ2VfaX0gLSBsZW5ndGhfe3llYXIsdHlwZX1ee3JlcGxhY2VtZW50X2l9CiQkIFdoZXJlOgoKLSAgICRsZW5ndGhfe3llYXIsdHlwZX1ee2luc3RhbGx9JCBhcmUgdGhlIHllYXJseSBjdW11bGF0aXZlIHJvYWQgbGVuZ3RocyBmb3IgYW4gaW5mcmFzdHJ1Y3R1cmUgJHR5cGUkIGluc3RhbGxhdGlvbgotICAgJGxlbmd0aF97eWVhcix0eXBlfV57Y2hhbmdlX2l9JCBhcmUgdGhlIHllYXJseSBjdW11bGF0aXZlIHJvYWQgbGVuZ3RocyBmb3IgYW4gaW5mcmFzdHJ1Y3R1cmUgJHR5cGUkIGNoYW5nZSBpbiBvcmRlciAkaSQKLSAgICRsZW5ndGhfe3llYXIsdHlwZX1ee3JlcGxhY2VtZW50X2l9JCBhcmUgdGhlIHllYXJseSBjdW11bGF0aXZlIHJvYWQgbGVuZ3RocyBmb3IgYW4gaW5mcmFzdHJ1Y3R1cmUgJHR5cGUkIHJlcGxhY2VkIGJ5IGNoYW5nZSBpbiBvcmRlciAkaSQKCmBgYHtyfQoKIycgQ2FsY3VsYXRlIFllYXJseSBBZGp1c3RlZCBSb2FkIExlbmd0aHMgQnkgSW5mcmFzdHJ1Y3R1cmUgVHlwZQojJyAKIycgQ2FsY3VsYXRlcyB0aGUgY3VtdWxhdGl2ZSB5ZWFybHkgYWRqdXN0ZWQgcm9hZCBsZW5ndGhzIGJ5IGluZnJhc3RydWN0dXJlIHR5cGUgYWNjb3VudGluZyBmb3IgaW5zdGFsbGF0aW9ucyBhbmQgc3Vic2VxdWVudCBjaGFuZ2VzLgojJwojJyBAcGFyYW0gZGYgQSBkYXRhLmZyYW1lIHdpdGggdGhyZWUgY29sdW1ucyBjb250YWluaW5nIHRoZSB5ZWFyLCB0eXBlLCBhbmQgcm9hZCBsZW5ndGhzLgojJyBAcGFyYW0geWVhcl9jb2xzIEEgdmVjdG9yIG9mIHRoZSBuYW1lcyAoY2hhcikgb3IgaW5kaWNlcyAoaW50KSBvZiB0aGUgY29sdW1ucyBjb250YWluaW5nIHRoZSB5ZWFycyBvZiBpbnN0YWxsYXRpb25zIGZvbGxvd2VkIGJ5IGluZnJhc3RydWN0dXJlIGNoYW5nZXMgaW4gb3JkZXIuCiMnIEBwYXJhbSB0eXBlX2NvbHMgQSB2ZWN0b3Igb2YgdGhlIG5hbWVzIChjaGFyKSBvciBpbmRpY2VzIChpbnQpIG9mIHRoZSBjb2x1bW5zIGNvbnRhaW5pbmcgdGhlIGluZnJhc3RydWN0dXJlIHR5cGVzIG9mIGluc3RhbGxhdGlvbnMgZm9sbG93ZWQgYnkgaW5mcmFzdHJ1Y3R1cmUgY2hhbmdlcyBpbiBvcmRlci4KIycgQHBhcmFtIHR5cGVfY29sIFRoZSBuYW1lIChjaGFyKSBvZiB0aGUgY29sdW1uIGNvbnRhaW5pbmcgdGhlIHR5cGUuCiMnIEBwYXJhbSBsZW5fY29scyBBIHZlY3RvciBvZiB0aGUgbmFtZXMgKGNoYXIpIG9yIGluZGljZXMgKGludCkgb2YgdGhlIGNvbHVtbnMgY29udGFpbmluZyB0aGUgcm9hZCBsZW5ndGhzIG9mIGluc3RhbGxhdGlvbnMgZm9sbG93ZWQgYnkgaW5mcmFzdHJ1Y3R1cmUgY2hhbmdlcyBpbiBvcmRlci4KIycgQHBhcmFtIG91dF9jb2xzIFRoZSBuYW1lIChjaGFyKSBvZiB0aGUgY29sdW1uIGNvbnRhaW5pbmcgdGhlIGNhbGN1bGF0ZWQgeWVhcmx5IHJvYWQgbGVuZ3RocyBieSB0eXBlLgojJyBAcGFyYW0gb3V0X2NvbCBUaGUgbmFtZSAoY2hhcikgb2YgdGhlIGNvbHVtbiBjb250YWluaW5nIHRoZSBjYWxjdWxhdGVkIHllYXJseSBhZGp1c3RlZCByb2FkIGxlbmd0aHMgYnkgdHlwZS4KIycgQHBhcmFtIHJlcGxfc3VmZml4IEEgc3VmZml4IChjaGFyKSB0byBhcHBlbmQgdG8gdGhlIGNvbHVtbnMgcmVwcmVzZW50aW5nIHRoZSByb2FkIGxlbmd0aHMgb2YgcmVwbGFjZWQgaW5mcmFzdHJ1Y3R1cmUgdHlwZXMgZnJvbSBjaGFuZ2VzLgojJyBAcGFyYW0gLi4uIEFkZGl0aW9uYWwgYXJndW1lbnRzIHBhc3NlZCB0byBjYWxjX3llYXJseV9sZW4uCiMnIAojJyBAcmV0dXJuIEEgZGF0YS5mcmFtZSB3aXRoIGNvbHVtbnMgY29udGFpbmluZyB0aGUgeWVhciwgdHlwZSwgY3VtdWxhdGl2ZSByb2FkIGxlbmd0aHMgb2YgaW5zdGFsbGF0aW9ucywgY2hhbmdlcywgYW5kIHJlcGxhY2VtZW50cywgYW5kIGNhbGN1bGF0ZWQgeWVhcmx5IGFkanVzdGVkIHJvYWQgbGVuZ3RocyBieSB0eXBlLgojJyBAZXhwb3J0CiMnCmNhbGNfeWVhcmx5X2Fkal9sZW4gPC0gZnVuY3Rpb24oCgkJZGYsCgkJeWVhcl9jb2xzID0gYygiaW5zdGFsbF95ZWFyIiwgInVwZ3JhZGUxX3llYXIiLCAidXBncmFkZTJfeWVhciIpLAoJCXR5cGVfY29scyA9IGMoImluc3RhbGxfdHlwZSIsICJ1cGdyYWRlMV90eXBlIiwgInVwZ3JhZGUyX3R5cGUiKSwKCQl0eXBlX2NvbCA9ICJ0eXBlIiwKCQlsZW5fY29scyA9ICJzZWdtZW50X2xlbiIsCgkJb3V0X2NvbHMgPSBjKCJpbnN0YWxsX2xlbiIsICJ1cGdyYWRlMV9sZW4iLCAidXBncmFkZTJfbGVuIiksCgkJb3V0X2NvbCA9ICJhZGpfbGVuIiwKCQlyZXBsX3N1ZmZpeCA9ICJfcmVwbGFjZWQiLAoJCS4uLgoJKSB7CgkKCSMgQ29udmVydCBsZW5fY29sIGlmIGNoYXIKCWxlbl9jb2xzIDwtIHJlcChsZW5fY29scywgbGVuZ3RoKHllYXJfY29scykpCgkKCSMgQ2hlY2sgY29scyBzYW1lIHNpemUKCXllYXJfY29sc19uIDwtIGxlbmd0aCh5ZWFyX2NvbHMpCgl0eXBlX2NvbHNfbiA8LSBsZW5ndGgodHlwZV9jb2xzKQoJbGVuX2NvbHNfbiA8LSBsZW5ndGgobGVuX2NvbHMpCglvdXRfY29sc19uIDwtIGxlbmd0aChvdXRfY29scykKCWlmIChsZW5ndGgodW5pcXVlKGMoeWVhcl9jb2xzX24sIHR5cGVfY29sc19uLCBsZW5fY29sc19uLCBvdXRfY29sc19uKSkpICE9IDEpIHsKCQlzdG9wKGdsdWUoCgkJCSJUaGUgYXJndW1lbnRzICd5ZWFyX2NvbHMnICh7eWVhcl9jb2xzX259KSwgJ3R5cGVfY29scycgKHt0eXBlX2NvbHNfbn0pLCAnbGVuX2NvbHMnICh7bGVuX2NvbHNfbn0pLCBhbmQgJ291dF9jb2xzJyAoe291dF9jb2xzX259KSBtdXN0IGJlIHRoZSBzYW1lIGxlbmd0aC4iCgkJKSkKCX0KCQoJIyBDYWxjIHllYXJseSBsZW5zIGJ5IGluZnJhIHR5cGUgcGVyIGluc3RhbGwgb3IgY2hhbmdlCglvdXQgPC0gbGlzdCgpCglmb3IgKGkgaW4gMTpsZW5ndGgoeWVhcl9jb2xzKSkgewoJCQoJCSMgR2V0IHllYXIsIHR5cGUsIGFuZCBsZW4gY29scwoJCXljb2wgPC0geWVhcl9jb2xzW1tpXV0KCQl0Y29sIDwtIHR5cGVfY29sc1tbaV1dCgkJbGNvbCA8LSBsZW5fY29sc1tbaV1dCgkJb2NvbCA8LSBvdXRfY29sc1tbaV1dCgkJCgkJIyBDYWxjIHllYXJseSBsZW4gZm9yIGluc3RhbGwgb3IgY2hhbmdlCgkJb3V0IDwtIGFwcGVuZCgKCQkJb3V0LAoJCQljYWxjX3llYXJseV9sZW4oCgkJCQlkZiwKCQkJCXllYXJfY29sID0geWNvbCwKCQkJCXR5cGVfY29sID0gdGNvbCwKCQkJCWxlbl9jb2wgPSBsY29sLAoJCQkJb3V0X2NvbCA9IG9jb2wsCgkJCQkuLi4KCQkJKSAlPiUKCQkJCXJlbmFtZSgKCQkJCQkieWVhciIgOj0gISF5Y29sLAoJCQkJCSJ0eXBlIiA6PSAhIXRjb2wKCQkJCSkgJT4lIGxpc3QKCQkpCgkJCgkJIyBDYWxjIHllYXJseSBsZW4gZm9yIHJlcGxhY2VtZW50CgkJaWYgKGkgPiAxKSB7CgkJCQoJCQkjIEdldCByZXBsIGNvbHMKCQkJdGNvbF9yZXBsIDwtIHR5cGVfY29sc1tbaSAtIDFdXQoJCQlsY29sX3JlcGwgPC0gbGVuX2NvbHNbW2kgLSAxXV0KCQkJCgkJCSMgRmlsdGVyIGZvciByZXBsIHJlY29yZHMgb25seSB3aGVyZSB0eXBlIGlzIG5vdCBlcSB0byBjaGFuZ2UgdHlwZQoJCQlkZl9yZXBsIDwtIGRmICU+JSBmaWx0ZXIoLmRhdGFbW3Rjb2xdXSAhPSAuZGF0YVtbdGNvbF9yZXBsXV0pCgkJCQoJCQkjIENhbGMgcmVwbCBsZW4gaWYgdGhlcmUgYXJlIGFueSBjaGFuZ2VzCgkJCWhhc19jaGFuZ2UgPC0gIWlzLm5hKGRmX3JlcGxbW3Rjb2xdXSkgJT4lIGFsbCAKCQkJaWYgKGhhc19jaGFuZ2UpIHsKCQkJCW91dCA8LSBhcHBlbmQoCgkJCQkJb3V0LAoJCQkJCWNhbGNfeWVhcmx5X2xlbigKCQkJCQkJZGZfcmVwbCwKCQkJCQkJeWVhcl9jb2wgPSB5Y29sLAoJCQkJCQl0eXBlX2NvbCA9IHRjb2xfcmVwbCwKCQkJCQkJbGVuX2NvbCA9IGxjb2xfcmVwbCwKCQkJCQkJb3V0X2NvbCA9IGdsdWUoIntvY29sfXtyZXBsX3N1ZmZpeH0iKSwKCQkJCQkJLi4uCgkJCQkJKSAlPiUKCQkJCQlyZW5hbWUoCgkJCQkJCSJ5ZWFyIiA6PSAhIXljb2wsCgkJCQkJCSJ0eXBlIiA6PSAhIXRjb2xfcmVwbAoJCQkJCSkgJT4lIGxpc3QKCQkJCSkKCQkJfQoJCX0KCX0KCQoJIyBDb21iaW5lIGFsbCBsZW5zIGluIGxpc3QgdG8gc2luZ2xlIGRmCglvdXQgPC0gb3V0ICU+JQoJCXJlZHVjZSgKCQkJbGVmdF9qb2luLCBieSA9IGMoInllYXIiLCAidHlwZSIpCgkJKSAlPiUKCQl1bmdyb3VwKCkKCQoJIyBDcmVhdGUgdGVtcGxhdGUgZm9yIGNoYW5nZSBhbmQgcmVwbCBjb2xzCgljaGFuZ2VfY29scyA8LSBwYXN0ZTAob3V0X2NvbHNbMjpvdXRfY29sc19uXSkjIGNoYW5nZSBjb2xzCgljaGFuZ2VfY29scyA8LSBjKGNoYW5nZV9jb2xzLCBwYXN0ZTAob3V0X2NvbHNbMjpvdXRfY29sc19uXSwgcmVwbF9zdWZmaXgpKSAjIHJlcGwgY29scwoJY2hhbmdlX2NvbHNfYWRkIDwtIHJlcCgwLCBsZW5ndGgoY2hhbmdlX2NvbHMpKSAjIHNldCBkZWZhdWx0IHZhbHMKCW5hbWVzKGNoYW5nZV9jb2xzX2FkZCkgPC0gY2hhbmdlX2NvbHMKCQoJIyBBZGQgY2hhbmdlIGFuZCByZXBsIGNvbHMgc2V0IHRvIDAgaWYgbm90IHByZXNlbnQKCW91dCA8LSBvdXQgJT4lIGFkZF9jb2x1bW4oCgkJISEhY2hhbmdlX2NvbHNfYWRkW3NldGRpZmYobmFtZXMoY2hhbmdlX2NvbHNfYWRkKSwgbmFtZXMoLikpXQoJKQoJCgkjIFNldCBOQSB0byAwCglvdXQgPC0gb3V0ICU+JSBtdXRhdGUoCgkJYWNyb3NzKGV2ZXJ5dGhpbmcoKSwgfnJlcGxhY2VfbmEoLiwgMCkpCgkpCgkKCSMgQ2FsYyB5ZWFybHkgYWRqIGxlbnMgYnkgaW5mcmEgdHlwZQoJb3V0IDwtIG91dCAlPiUKCQltdXRhdGUoICMgYWRkZWQgbGVuIGJ5IGluZnJhIHR5cGVzIGR1ZSB0byBpbnN0YWxsIG9yIGNoYW5nZXMKCQkJISFvdXRfY29sIDo9IHJlZHVjZShhY3Jvc3MoYWxsX29mKG91dF9jb2xzKSksIGArYCkKCQkpICU+JQoJCW11dGF0ZSggIyByZW1vdmVkIGxlbiBieSBpbmZyYSB0eXBlcyBkdWUgdG8gcmVwbGFjZW1lbnRzCgkJCSEhb3V0X2NvbCA6PSAuZGF0YVtbb3V0X2NvbF1dIC0gcmVkdWNlKAoJCQkJYWNyb3NzKGFsbF9vZigKCQkJCQlwYXN0ZTAob3V0X2NvbHNbMjpvdXRfY29sc19uXSwgcmVwbF9zdWZmaXgpCgkJCQkpKSwKCQkJCWAtYAoJCQkpCgkJKQoJCgkjIFJlbmFtZSB0eXBlIGNvbAoJb3V0IDwtIG91dCAlPiUgcmVuYW1lKCEhdHlwZV9jb2wgOj0gdHlwZSkKCXJldHVybihvdXQpCn0KYGBgCgojIyBGdW5jdGlvbiAzIChwbG90X3llYXJseV9sZW4pOiBQbG90IExlbmd0aHMgYnkgWWVhciBmb3IgR2VuZXJpYyBUeXBlcwoKUGxvdHMgYW4gYXJlYSBjaGFydCBzaG93aW5nIHRoZSBjdW11bGF0aXZlIHJvYWQgbGVuZ3RocyBieSBhIHVzZXItZGVmaW5lZCB0eXBlIGZvciBlYWNoIHllYXIuCgpUaGlzIGlzIGEgZ2VuZXJpYyBmdW5jdGlvbiBmb3IgdXNlci1kZWZpbmVkIHR5cGVzIHN1Y2ggYXMgaW5mcmFzdHJ1Y3R1cmUgb3Igcm9hZCB0eXBlcy4KCmBgYHtyfQoKIycgUGxvdCBZZWFybHkgUm9hZCBMZW5ndGhzIEJ5IFR5cGUKIycgCiMnIENyZWF0ZXMgYW4gYXJlYSBwbG90IG9mIHJvYWQgbGVuZ3RocyBieSBjYXRlZ29yeSB0eXBlcy4KIycKIycgQHBhcmFtIGRmIEEgZGF0YS5mcmFtZSB3aXRoIHRocmVlIGNvbHVtbnMgY29udGFpbmluZyB0aGUgeWVhciwgdHlwZSwgYW5kIHJvYWQgbGVuZ3Rocy4KIycgQHBhcmFtIHRpdGxlIFRoZSB0aXRsZSAoY2hhcikgb2YgdGhlIHBsb3QuCiMnIEBwYXJhbSB0aXRsZV91bmRlcmxpbmUgU2V0IHRvIFRSVUUgdG8gdW5kZXJsaW5lIHRoZSB0aXRsZS4KIycgQHBhcmFtIHhfdGl0bGUgVGhlIHRpdGxlIChjaGFyKSBvZiB0aGUgeC1heGlzLgojJyBAcGFyYW0geV90aXRsZSBUaGUgdGl0bGUgKGNoYXIpIG9mIHRoZSB5LWF4aXMuCiMnIEBwYXJhbSB5X3N1ZmZpeCBUaGUgc3VmZml4IChjaGFyKSB0byBhZGQgdG8gdGhlIGVuZCBvZiB5IGF4aXMgdmFsdWVzLgojJyBAcGFyYW0gbGVnZW5kX3RpdGxlIFRoZSB0aXRsZSAoY2hhcikgb2YgdGhlIGxlZ2VuZC4KIycgQHBhcmFtIGxlZ2VuZCBTZXQgdG8gVFJVRSB0byBpbmNsdWRlIGEgbGVnZW5kLgojJyBAcGFyYW0geWVhcl9jb2wgVGhlIG5hbWUgKGNoYXIpIG9yIGluZGV4IChpbnQpIG9mIHRoZSBjb2x1bW4gY29udGFpbmluZyB0aGUgeWVhcnMuCiMnIEBwYXJhbSB5ZWFyX21pbiBUaGUgbWluaW11bSB5ZWFyIChpbnQpIHRvIGRpc3BsYXkuCiMnIEBwYXJhbSB5ZWFyX21heCBUaGUgbWF4aW11bSB5ZWFyIChpbnQpIHRvIGRpc3BsYXkuCiMnIEBwYXJhbSB5ZWFyX2ludCBUaGUgeWVhciBpbnRlcnZhbHMgKGludCkgdG8gZGlzcGxheS4gRm9yIGV4YW1wbGUsIDEgZGlzcGxheXMgZXZlcnkgeWVhciwgYW5kIDIgZGlzcGxheXMgZXZlcnkgdHdvIHllYXJzLgojJyBAcGFyYW0gbGVuX2NvbCBUaGUgbmFtZSAoY2hhcikgb3IgaW5kZXggKGludCkgb2YgdGhlIGNvbHVtbiBjb250YWluaW5nIHRoZSByb2FkIGxlbmd0aHMuCiMnIEBwYXJhbSB0eXBlX2NvbCBUaGUgbmFtZSAoY2hhcikgb3IgaW5kZXggKGludCkgb2YgdGhlIGNvbHVtbiBjb250YWluaW5nIHRoZSB0eXBlLgojJyBAcGFyYW0gdHlwZV9maWx0ZXIgQSB2ZWN0b3IgKGNoYXIpIG9mIHR5cGVzIHRvIHJlbW92ZSBmb21yIHRoZSBwbG90LgojJyBAcGFyYW0gdHlwZV9yZWNvZGUgQSBuYW1lZCB2ZWN0b3IgKGNoYXIpIG9mIG5hbWVzIHJlcHJlc2VudGluZyB0eXBlcyBhbmQgdmFsdWVzIHJlcHJlc2VudGluZyB0aGUgdmFsdWVzIHRvIHJlcGxhY2UgZWFjaCB0eXBlIHdpdGguCiMnIEBwYXJhbSBsaW5lXzUwa20gU2V0IHRvIFRSVUUgdG8gZHJhdyB0aGUgNTAga20gcmVkIHJlZmVyZW5jZSBsaW5lLgojJyBAcGFyYW0gbGluZV95ZWFyIFNldCB0byBhIHllYXIgKGludCkgdG8gZHJhdyBhIHJlZmVyZW5jZSBsaW5lIGZvciBhIHllYXIuIElmIEZBTFNFLCBhIGxpbmUgd2lsbCBub3QgYmUgZHJhd24uCiMnIEBwYXJhbSBjb2xvcl9sb3cgVGhlIGJvdHRvbSBjb2xvciAoY2hhcikgb2YgdGhlIHR5cGUuCiMnIEBwYXJhbSBjb2xvcl9oaWdoIFRoZSB0b3AgY29sb3IgKGNoYXIpIG9mIHRoZSB0eXBlLgojJyBAcmV0dXJuIEFuIGFyZWEgZ2dwbG90IG9mIHRoZSBjdW11bGF0aXZlIHllYXJseSByb2FkIGxlbmd0aHMgYnkgdHlwZS4KIycgQGV4cG9ydAojJwpwbG90X3llYXJseV9sZW4gPC0gZnVuY3Rpb24oCiAgICAgICAgZGYsCiAgICAgICAgdGl0bGUgPSAiIiwKICAgICAgICB0aXRsZV91bmRlcmxpbmUgPSBUUlVFLAogICAgICAgIHhfdGl0bGUgPSAiIiwKICAgICAgICB5X3RpdGxlID0gIiIsCiAgICAgICAgeV9zdWZmaXggPSAiIGttIiwKICAgICAgICBsZWdlbmRfdGl0bGUgPSAiVHlwZSIsCiAgICAgICAgbGVnZW5kID0gVFJVRSwKICAgICAgICB5ZWFyX2NvbCA9ICJ5ZWFyIiwKICAgICAgICB5ZWFyX21pbiA9IEZBTFNFLAogICAgICAgIHllYXJfbWF4ID0gRkFMU0UsCiAgICAgICAgeWVhcl9pbnQgPSAxLAogICAgICAgIGxlbl9jb2wgPSAiYWRqX2xlbiIsCiAgICAgICAgdHlwZV9jb2wgPSAidHlwZSIsCiAgICAgICAgdHlwZV9maWx0ZXIgPSBjKCksCiAgICAgICAgdHlwZV9yZWNvZGUgPSBjKCksCiAgICAgICAgbGluZV81MGttID0gRkFMU0UsCiAgICAgICAgbGluZV95ZWFyID0gRkFMU0UsCiAgICAgICAgY29sb3JfbG93ID0gIiNERkVCRjciLAogICAgICAgIGNvbG9yX2hpZ2ggPSAiIzM2ODNCQiIKKSB7CgkKCSMgRmlsdGVyIHRvIHN0YXJ0IGFuZCBlbmQgeWVhcnMKCWlmICh5ZWFyX21pbiA+IDApIHsKCQlkZiA8LSBkZiAlPiUgZmlsdGVyKAoJCQkuZGF0YVtbeWVhcl9jb2xdXSA+PSB5ZWFyX21pbgoJCSkKCX0KCWlmICh5ZWFyX21heCA+IDApIHsKCQlkZiA8LSBkZiAlPiUgZmlsdGVyKAoJCQkuZGF0YVtbeWVhcl9jb2xdXSA8PSB5ZWFyX21heAoJCSkKCX0KCQoJIyBGaWx0ZXIgb3V0IHBhcnRpY3VsYXIgaW5mcmFzdHJ1Y3R1cmUgdHlwZXMKCWlmIChsZW5ndGgodHlwZV9maWx0ZXIpID4gMCkgewoJCWRmIDwtIGRmICU+JSBmaWx0ZXIoCgkJCSEuZGF0YVtbdHlwZV9jb2xdXSAlaW4lIHR5cGVfZmlsdGVyCgkJKQoJfQoJCgkjIFJlY29kZSBhbmQgcmVvcmRlciBjYXRlZ29yeSB0eXBlcwoJaWYgKGxlbmd0aCh0eXBlX3JlY29kZSkgPiAwKSB7CgkJCgkJIyBSZW9yZGVyIGNhdGVnb3J5IHR5cGVzCgkJdHlwZV91bmlxIDwtIHVuaXF1ZShkZltbdHlwZV9jb2xdXSkKCQl0eXBlX3Jlb3JkZXIgPC0gbmFtZXModHlwZV9yZWNvZGUpCgkJdHlwZV9yZW9yZGVyIDwtIGModHlwZV9yZW9yZGVyLCB0eXBlX3VuaXFbIXR5cGVfdW5pcSAlaW4lIHR5cGVfcmVvcmRlcl0pCgkJZGZbW3R5cGVfY29sXV0gPC0gZmFjdG9yKGRmW1t0eXBlX2NvbF1dLCBsZXZlbHMgPSB0eXBlX3Jlb3JkZXIpCgkJCgkJIyBSZWNvZGUgY2F0ZWdvcnkgdHlwZXMKCQlkZltbdHlwZV9jb2xdXSA8LSByZWNvZGUoZGZbW3R5cGVfY29sXV0sICEhIXR5cGVfcmVjb2RlKQoJfQoJCgkjIENyZWF0ZSBmaWxsIGNvbG9ycwoJdHlwZV9uIDwtIGxlbmd0aCh0eXBlX3VuaXEpCgl0eXBlX2NvbG9ycyA8LSBzY2FsZXM6OnNlcV9ncmFkaWVudF9wYWwoCgkJY29sb3JfbG93LAoJCWNvbG9yX2hpZ2gKCSkoc2VxKDAsIDEsIGxlbmd0aC5vdXQgPSB0eXBlX24pKQoJCgkjIENyZWF0ZSBiYXNlIGFyZWEgcGxvdCB3aXRoIGxlZ2VuZCBhbmQgbGFiZWxzCglsZW5fbWF4IDwtIG1heChkZltbbGVuX2NvbF1dLCBuYS5ybSA9IFRSVUUpCgl5ZWFyX21heCA8LSBtYXgoZGZbW3llYXJfY29sXV0sIG5hLnJtID0gVFJVRSkKCW91dCA8LSBnZ3Bsb3QoCgkJZGYsCgkJYWVzKAoJCQl4ID0gLmRhdGFbW3llYXJfY29sXV0sCgkJCXkgPSAuZGF0YVtbbGVuX2NvbF1dLAoJCQlmaWxsID0gLmRhdGFbW3R5cGVfY29sXV0sCgkJCW9yZGVyID0gZGVzYyguZGF0YVtbdHlwZV9jb2xdXSkKCQkpCgkpICsKCWdlb21fYXJlYShjb2xvdXIgPSBOQSwgYWxwaGEgPSAwLjcpICsKCXNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHR5cGVfY29sb3JzKSArCglnZW9tX2xpbmUoCgkJcG9zaXRpb24gPSAic3RhY2siLAoJCXNpemUgPSAwLjIKCSkgKwoJbGFicygKCQl4ID0geF90aXRsZSwKCQl5ID0geV90aXRsZSwKCQlmaWxsID0gbGVnZW5kX3RpdGxlCgkpICsKCWd1aWRlcygKCQlmaWxsID0gRkFMU0UsCgkJY29sb3IgPSBGQUxTRQoJKSArCglzY2FsZV94X2NvbnRpbnVvdXMoCgkJYnJlYWtzID0gc2VxKHllYXJfbWluLCB5ZWFyX21heCwgYnkgPSB5ZWFyX2ludCksCgkJbGFiZWxzID0gc2VxKHllYXJfbWluLCB5ZWFyX21heCwgYnkgPSB5ZWFyX2ludCksCgkJbGltaXRzID0gYyh5ZWFyX21pbiwgeWVhcl9tYXgpCgkpICsKCXNjYWxlX3lfY29udGludW91cygKCQlsYWJlbCA9IHNjYWxlczo6bGFiZWxfbnVtYmVyKHN1ZmZpeCA9IHlfc3VmZml4KQoJKSArCgl0aGVtZV9taW5pbWFsKCkgKwoJdGhlbWUoCgkJcGxvdC5tYXJnaW4gPSB1bml0KGMoNSw1LDUsNSksICJwb2ludHMiKQoJKQoJCgkjIEFkZCB0aXRsZQoJaWYgKHRpdGxlX3VuZGVybGluZSkgewoJCW91dCA8LSBvdXQgKyBnZ3RpdGxlKAoJCQlicXVvdGUodW5kZXJsaW5lKC4odGl0bGUpKSkKCQkpCgl9IGVsc2UgewoJCW91dCA8LSBvdXQgKyBnZ3RpdGxlKHRpdGxlKQoJfQoJCgkjIEFkZCBsZWdlbmQKCWlmIChsZWdlbmQpIHsKCQlvdXQgPC0gb3V0ICsgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQoCgkJCXJldmVyc2UgPSBGQUxTRSwKCQkJb3ZlcnJpZGUuYWVzID0gbGlzdCgKCQkJCWFscGhhID0gMC43LAoJCQkJY29sb3IgPSBOQSwKCQkJCXNoYXBlID0gTkEKCQkJKQoJCSkpCgl9CgkKCSMgQWRkIGRvdHRlZCB5ZWFyIHJlZiBsaW5lCglpZiAobGluZV95ZWFyKSB7CgkJb3V0IDwtIG91dCArIGdlb21fdmxpbmUoCgkJCXhpbnRlcmNlcHQgPSBsaW5lX3llYXIsCgkJCWNvbG9yID0gImJsYWNrIiwKCQkJbGluZXR5cGUgPSAiZGFzaGVkIgoJCSkKCX0KCQoJIyBBZGQgcmVkIDUwa20gcmVmIGxpbmUKCWlmIChsaW5lXzUwa20pIHsKCQlvdXQgPC0gb3V0ICsgZ2VvbV9zZWdtZW50KCAjIDUwa20gcmVkIGxpbmUKCQkJYWVzKAoJCQkJeCA9IDIwMDksCgkJCQl5ID0gMCwKCQkJCXhlbmQgPSAyMDA5LAoJCQkJeWVuZCA9IDUwLAoJCQkJY29sb3IgPSAiI2JiMDAwMCIsCgkJCQloanVzdCA9IDAuMTUKCQkJKQoJCSkgKwoJCWdlb21fc2VnbWVudCggIyA1MGttIHJlZCB0cmlhbmdsZSBwb2ludCBkb3duCgkJCWFlcygKCQkJCXggPSAyMDA5LAoJCQkJeSA9IDUwLjAxIC0gKGxlbl9tYXggKiAwLjA1KSwKCQkJCXhlbmQgPSAyMDA5LAoJCQkJeWVuZCA9IDUwIC0gKGxlbl9tYXggKiAwLjA1KSwKCQkJCWNvbG9yID0gIiNiYjAwMDAiLAoJCQkJaGp1c3QgPSAwLjE1CgkJCSksCgkJCWFycm93ID0gYXJyb3coCgkJCQlsZW5ndGggPSB1bml0KDAuMDMsICJucGMiKSwKCQkJCWVuZHMgPSAibGFzdCIsCgkJCQl0eXBlID0gImNsb3NlZCIKCQkJKQoJCSkgKwoJCWdlb21fc2VnbWVudCggIyA1MGttIHJlZCB0cmlhbmdsZSBwb2ludCB1cAoJCQlhZXMoCgkJCQl4ID0gMjAwOSwKCQkJCXkgPSAobGVuX21heCAqIDAuMDUpIC0gMC4wMSwKCQkJCXhlbmQgPSAyMDA5LAoJCQkJeWVuZCA9IChsZW5fbWF4ICogMC4wNSksCgkJCQljb2xvciA9ICIjYmIwMDAwIiwKCQkJCWhqdXN0ID0gMC4xNQoJCQkpLAoJCQlhcnJvdyA9IGFycm93KAoJCQkJbGVuZ3RoID0gdW5pdCgwLjAzLCAibnBjIiksCgkJCQllbmRzID0gImxhc3QiLAoJCQkJdHlwZSA9ICJjbG9zZWQiCgkJCSkKCQkpICsKCQlhbm5vdGF0ZSgKCQkJInRleHQiLAoJCQl4ID0gMjAwOSwKCQkJeSA9IDUwLAoJCQlsYWJlbCA9ICI1MGttIiwKCQkJY29sb3IgPSAiI2JiMDAwMCIsCgkJCWhqdXN0ID0gLTAuMjI1CgkJKQoJfQoJcmV0dXJuKG91dCkKfQpgYGAKCiMjIEZ1bmN0aW9uIDQgKHBsb3RfeWVhcmx5X2xlbl9pbmZyYSk6IFBsb3QgTGVuZ3RocyBieSBZZWFyIGZvciBJbmZyYXN0cnVjdHVyZSBUeXBlcwoKUGxvdHMgYXJlYSBjaGFydHMgb2YgeWVhcmx5IHJvYWQgbGVuZ3RocyBieSBpbmZyYXN0cnVjdHVyZSB0eXBlcyBmb3IgYSBsaXN0IG9mIGRhdGEuCgpUaGlzIHVzZXMgdGhlIGBwbG90X3llYXJseV9sZW5gIGZ1bmN0aW9uLgoKYGBge3J9CgojJyBQbG90IFllYXJseSBSb2FkIExlbmd0aHMgQnkgSW5mcmFzdHJ1Y3R1cmUgVHlwZQojJyAKIycgQ3JlYXRlcyBhcmVhIHBsb3RzIG9mIHJvYWQgbGVuZ3RocyBieSBpbmZyYXN0cnVjdHVyZSB0eXBlLgojJwojJyBAcGFyYW0gZGZfbGlzdCBBIGxpc3Qgb2YgZGF0YS5mcmFtZSBjb250YWluaW5nIHRoZSBpbnN0YWxsIGFuZCBjaGFuZ2UgeWVhcnMsIHR5cGUsIGFuZCByb2FkIHNlZ21lbnQgbGVuZ3Rocy4KIycgQHJldHVybiBNdWx0aXBsZSBhcmVhIGdncGxvdHMgb2YgdGhlIGN1bXVsYXRpdmUgeWVhcmx5IHJvYWQgbGVuZ3RocyBieSBpbmZyYXN0cnVjdHVyZSB0eXBlIGNvbWJpbmVkIHdpdGggcGF0Y2h3b3JrLgojJyBAZXhwb3J0CiMnCnBsb3RfeWVhcmx5X2xlbl9pbmZyYSA8LSBmdW5jdGlvbihkZl9saXN0KSB7CgkKCSMgQ3JlYXRlIGluZnJhIHBsb3RzIGZyb20gZGF0YQoJcCA8LSBsaXN0KCkKCWZvciAoaSBpbiAxOmxlbmd0aChkZl9saXN0KSkgewoJCQoJCSMgR2V0IGRhdGEgYW5kIHBsb3QgdGl0bGUKCQlkZiA8LSBkZl9saXN0W1tpXV0KCQlwdGl0bGUgPC0gbmFtZXMoZGZfbGlzdClbW2ldXQoJCQoJCSMgQ3JlYXRlIGFuZCBhZGQgaW5mcmEgcGxvdCB0byBsaXN0CgkJcFtbaV1dIDwtIGNhbGNfeWVhcmx5X2Fkal9sZW4oZGYsIHR5cGVfY29sID0gc2V0dGluZ3MkdHlwZV9jb2xfaW5mcmEpICU+JQoJCSAgICBwbG90X3llYXJseV9sZW4oCgkJICAgIAl0aXRsZSA9IHB0aXRsZSwKCQkgICAgCXllYXJfbWluID0gc2V0dGluZ3MkeWVhcl9taW4sCgkJICAgICAgICB5ZWFyX21heCA9IHNldHRpbmdzJHllYXJfbWF4LAoJCQkJdHlwZV9jb2wgPSBzZXR0aW5ncyR0eXBlX2NvbF9pbmZyYSwKCQkJCXR5cGVfZmlsdGVyID0gc2V0dGluZ3MkdHlwZV9maWx0ZXJfaW5mcmEsCgkJICAgICAgICB0eXBlX3JlY29kZSA9IHNldHRpbmdzJHR5cGVfcmVjb2RlX2luZnJhLAoJCQkJbGVnZW5kX3RpdGxlID0gIkluZnJhc3RydWN0dXJlIFR5cGUiLAoJCQkJbGluZV81MGttID0gVFJVRSwKCQkgICAgICAgIGxpbmVfeWVhciA9IHNldHRpbmdzJGxpbmVfeWVhcgoJCSAgICApCgl9CgkKCSMgWS1heGlzIHRpdGxlCgl5X3RpdGxlIDwtIGdncGxvdCgpICsKCQlhbm5vdGF0ZSgKCQkJZ2VvbSA9ICJ0ZXh0IiwKCQkJeCA9IDEsCgkJCXkgPSAxLAoJCQlsYWJlbCA9ICJUb3RhbCBMZW5ndGggKENlbnRyZWxpbmUga20pIiwKCQkJYW5nbGUgPSA5MCwKCQkJc2l6ZSA9IDUKCQkpICsKCQljb29yZF9jYXJ0ZXNpYW4oY2xpcCA9ICJvZmYiKSsKCQl0aGVtZV92b2lkKCkKCQoJIyBDb21iaW5lIGFsbCBpbmZyYSBwbG90cyB0b2dldGhlcgoJb3V0IDwtICh5X3RpdGxlIHwgd3JhcF9wbG90cyhwLCBucm93ID0gbGVuZ3RoKHApKSkgKwoJCXBsb3RfYW5ub3RhdGlvbigKCQkJdGl0bGUgPSAiUm9hZHdheXMgd2l0aCBEZWRpY2F0ZWQgQ3ljbGluZyBJbmZyYXN0cnVjdHVyZSIsCgkJCWNhcHRpb24gPSBzcHJpbnRmKCJZZWFycyAoJXMtJXMpIiwgc2V0dGluZ3MkeWVhcl9taW4sIHNldHRpbmdzJHllYXJfbWF4KSwKCQkJdGhlbWUgPSB0aGVtZSgKCQkJCXBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNiksCgkJCQlwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNCkKCQkJKQoJCSkgKwoJCXBsb3RfbGF5b3V0KHdpZHRocyA9IGMoMC4wNSwgMSkpCglyZXR1cm4ob3V0KQp9CmBgYAoKIyMgRnVuY3Rpb24gNSAocGxvdF95ZWFybHlfbGVuX3JvYWQpOiBQbG90IExlbmd0aHMgYnkgWWVhciBmb3IgUm9hZCBUeXBlcwoKUGxvdHMgYXJlYSBjaGFydHMgb2YgeWVhcmx5IHJvYWQgbGVuZ3RocyBieSBvdmVyYWxsIHJvYWQgdHlwZSBhbmQgYnkgaW5mcmFzdHJ1Y3R1cmUgc2VwYXJhdGVkIGJ5IGVhY2ggcm9hZCB0eXBlLgoKVGhpcyB1c2VzIHRoZSBgcGxvdF95ZWFybHlfbGVuYCBmdW5jdGlvbi4KCmBgYHtyfQoKIycgUGxvdCBZZWFybHkgUm9hZCBMZW5ndGhzIEJ5IFJvYWQgVHlwZQojJwojJyBDcmVhdGVzIGFyZWEgcGxvdHMgb2Ygcm9hZCBsZW5ndGhzIGJ5IG92ZXJhbGwgcm9hZCB0eXBlLCBhbmQgYnkgaW5mcmFzdHJ1Y3R1cmUgcGVyIHJvYWQgdHlwZS4KIycKIycgQHBhcmFtIGRmIFRoZSBkYXRhLmZyYW1lIGNvbnRhaW5pbmcgdGhlIGluc3RhbGwgYW5kIGNoYW5nZSB5ZWFycywgdHlwZSwgYW5kIHJvYWQgc2VnbWVudCB0eXBlcyBhbmQgbGVuZ3Rocy4gCiMnIEByZXR1cm4gTXVsdGlwbGUgYXJlYSBnZ3Bsb3RzIG9mIHRoZSBjdW11bGF0aXZlIHllYXJseSByb2FkIGxlbmd0aHMgYnkgcm9hZCB0eXBlIGNvbWJpbmVkIHdpdGggcGF0Y2h3b3JrLgojJyBAZXhwb3J0CiMnCnBsb3RfeWVhcmx5X2xlbl9yb2FkIDwtIGZ1bmN0aW9uKGRmLCB0aXRsZSA9ICJSb2Fkd2F5cyB3aXRoIERlZGljYXRlZCBDeWNsaW5nIEluZnJhc3RydWN0dXJlIikgewoJCgkjIENyZWF0ZSBsaXN0IHRvIHN0b3JlIHBsb3RzCglwIDwtIGxpc3QoKQoKCSMgUGxvdCBvdmVyYWxsIHJvYWQgdHlwZXMKCXBbWzFdXSA8LSBjYWxjX3llYXJseV9sZW4oCgkJZGYsCgkJeWVhcl9jb2wgPSBzZXR0aW5ncyR5ZWFyX2NvbF9yb2FkLAoJCXR5cGVfY29sID0gc2V0dGluZ3MkdHlwZV9jb2xfcm9hZAoJKSAlPiUKCSAgICBwbG90X3llYXJseV9sZW4oCgkgICAgCXRpdGxlID0gdGl0bGUsCgkgICAgCXRpdGxlX3VuZGVybGluZSA9IEZBTFNFLAoJICAgIAl5ZWFyX2NvbCA9IHNldHRpbmdzJHllYXJfY29sX3JvYWQsCgkJCXllYXJfbWluID0gc2V0dGluZ3MkeWVhcl9taW4sCgkgICAgICAgIHllYXJfbWF4ID0gc2V0dGluZ3MkeWVhcl9tYXgsCgkJCXhfdGl0bGUgPSBzcHJpbnRmKCJZZWFycyAoJXMtJXMpIiwgc2V0dGluZ3MkeWVhcl9taW4sIHNldHRpbmdzJHllYXJfbWF4KSwKCQkJeV90aXRsZSA9ICJUb3RhbCBMZW5ndGggKENlbnRyZWxpbmUga20pIiwKCQkJbGVnZW5kX3RpdGxlID0gIlJvYWR3YXkgVHlwZSIsCgkJCXR5cGVfY29sID0gc2V0dGluZ3MkdHlwZV9jb2xfcm9hZCwKCQkJdHlwZV9yZWNvZGUgPSBzZXR0aW5ncyR0eXBlX3JlY29kZV9yb2FkLAoJCQlsZW5fY29sID0gImxlbiIsCgkJCWxpbmVfNTBrbSA9IEZBTFNFLAoJCQlsaW5lX3llYXIgPSBzZXR0aW5ncyRsaW5lX3llYXIsCgkgICAgICAgIGNvbG9yX2xvdyA9ICIjQzFEREIzIiwKCSAgICAgICAgY29sb3JfaGlnaCA9ICIjMjk3QTIyIgoJICAgICkgKwoJCXRoZW1lKAoJCQlwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCksCgkJCXBsb3QubWFyZ2luID0gbWFyZ2luKDAsIDAsIDAsIDAsICJwdCIpCgkJKQoJCgkjIFBsb3QgYXJ0ZXJpYWwsIGNvbGxlY3RvciwgYW5kIGxvY2FsIHJvYWQgYnkgaW5mcmEKCXJ0eXBlcyA8LSBjKCJBcnRlcmlhbCIsICJDb2xsZWN0b3IiLCAiTG9jYWwiKQoJZm9yIChpIGluIDE6bGVuZ3RoKHJ0eXBlcykpIHsKCQkKCQkjIEdldCByb2FkIHR5cGUKCQlyIDwtIHJ0eXBlc1tpXQoJCQoJCSMgQ3JlYXRlIGluZnJhIHBsb3QgZm9yIHJvYWQgdHlwZQoJCXBbW2kgKyAxXV0gPC0gY2FsY195ZWFybHlfYWRqX2xlbigKCQkJZGYgJT4lIGZpbHRlcihyb2FkX3R5cGUgPT0gciksCgkJCXR5cGVfY29sID0gc2V0dGluZ3MkdHlwZV9jb2xfaW5mcmEKCQkpICU+JQoJCQlwbG90X3llYXJseV9sZW4oCgkJCSAgICB0aXRsZSA9IHNwcmludGYoIiVzIFJvYWR3YXlzIiwgciksCgkJCSAgICB0aXRsZV91bmRlcmxpbmUgPSBGQUxTRSwKCQkJICAgIGxpbmVfNTBrbSA9IEZBTFNFLAoJCQkgICAgbGluZV95ZWFyID0gc2V0dGluZ3MkbGluZV95ZWFyLAoJCQkgICAgeWVhcl9pbnQgPSAyLAoJCQkgICAgeF90aXRsZSA9IHNwcmludGYoIlllYXJzICglcy0lcykiLCBzZXR0aW5ncyR5ZWFyX21pbiwgc2V0dGluZ3MkeWVhcl9tYXgpLAoJCQkgICAgeV90aXRsZSA9ICJUb3RhbCBMZW5ndGggKENlbnRyZWxpbmUga20pIiwKCQkJICAgIHllYXJfbWluID0gc2V0dGluZ3MkeWVhcl9taW4sCgkJICAgICAgICB5ZWFyX21heCA9IHNldHRpbmdzJHllYXJfbWF4LAoJCQkJdHlwZV9jb2wgPSBzZXR0aW5ncyR0eXBlX2NvbF9pbmZyYSwKCQkJCXR5cGVfZmlsdGVyID0gc2V0dGluZ3MkdHlwZV9maWx0ZXJfaW5mcmEsCgkJICAgICAgICB0eXBlX3JlY29kZSA9IHNldHRpbmdzJHR5cGVfcmVjb2RlX2luZnJhLAoJCQkJbGVnZW5kX3RpdGxlID0gIkluZnJhc3RydWN0dXJlIFR5cGUiCgkJCSkgKwoJCQl0aGVtZSgKCQkJCXBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKCQkJCXBsb3QubWFyZ2luID0gbWFyZ2luKDAsIDEyLCAwLCAwLCAicHQiKQoJCQkpCgl9CgkKCSMgUGxvdCBob3Jpem9udGFsIGdyYWRpZW50IGJhcgoJZ3JhZF9iYXIgPC0gIGdncGxvdChkYXRhLmZyYW1lKHggPSAxOjQpLCBhZXMoeCA9IHgsIHkgPSAxLCBjb2xvciA9IHgpKSArCgkJZ2VvbV9saW5lKHNpemUgPSA0KSArCgkJc2NhbGVfY29sb3JfZ3JhZGllbnQobG93ID0gIiNDMUREQjMiLCBoaWdoID0gIiMyOTdBMjIiKSArCgkJdGhlbWVfdm9pZCgpICsKCQlndWlkZXMoY29sb3IgPSBGQUxTRSkgKwoJCXRoZW1lKAoJCQlheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAoJICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCksCgkgICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksCgkgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwKCQkJcGxvdC5tYXJnaW4gPSBtYXJnaW4oMCwgMCwgMCwgMCwgInB0IikKCQkpCgkKCSMgUGxvdCBvdmVyYWxsIGFuZCByb2FkIHR5cGUgcGxvdHMgdG9nZXRoZXIKCW91dCA8LSAoICMgb3ZlcmFsbCBwbG90CgkJcGxvdF9zcGFjZXIoKSArCgkJcFtbMV1dICsKCQlwbG90X3NwYWNlcigpICsKCQlwbG90X2xheW91dCgKCQkJd2lkdGhzID0gYygwLjI1LCAwLjM1LCAwLjIpCgkJKQoJKSAvICggIyBncmFkaWVudCBiYXIKCQlwbG90X3NwYWNlcigpICsKCQlncmFkX2JhciArCgkJcGxvdF9zcGFjZXIoKSArCgkJcGxvdF9sYXlvdXQod2lkdGhzID0gYygtMC44LCAxMCwgLTEuMSkpCgkpIC8gKCAjIGluZnJhIHBsb3RzCgkJcFtbMl1dICsKCQlwW1szXV0gKwoJCXBbWzRdXQoJKSArIHBsb3RfbGF5b3V0KAoJCWhlaWdodHMgPSBjKDEyLCAxLCA4KQoJKSArIHBsb3RfYW5ub3RhdGlvbiggIyBBIEIgdGFncwoJCXRhZ19sZXZlbHMgPSBsaXN0KGMoIkEiLCAiIiwgIkIiLCAiIiwgIiIpKQoJKSAmIHRoZW1lKAoJCXBsb3QudGFnID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxMikKCSkKCXJldHVybihvdXQpCn0KYGBgCgojIyBGdW5jdGlvbiA2IChwbG90X3llYXJseV9kaWZmKTogUGxvdCBZZWFybHkgRGlmZmVyZW5jZXMKClBsb3RzIGEgYmFyIGNoYXJ0IG9mIGRpZmZlcmVuY2VzIGJldHdlZW4gdHdvIGNvbHVtbnMgY29udGFpbmluZyB5ZWFycy4KClRoaXMgZnVuY3Rpb24gaXMgdXNlZCB0byBjaGVjayB0aGUgZGlmZmVyZW5jZXMgaW4gaW5zdGFsbGF0aW9uIHllYXJzIGJldHdlZW4gdGhlIGNpdHkncyBkYXRhIGFuZCB0aGUgdmVyaWZpZWQgZGF0YS4KCmBgYHtyfQojJyBQbG90IFllYXJseSBEaWZmZXJlbmNlcwojJwojJyBDcmVhdGVzIGEgYmFyIHBsb3Qgb2YgdGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gdHdvIHllYXJzLgojJwojJyBAcGFyYW0gZGYgVGhlIGRhdGEuZnJhbWUgY29udGFpbmluZyB0aGUgdHdvIGNvbHVtbnMgd2l0aCB0aGUgeWVhcnMuCiMnIEBwYXJhbSB5ZWFyX2NvbDEgVGhlIG5hbWUgKGNoYXIpIG9yIGluZGV4IChpbnQpIG9mIHRoZSBmaXJzdCB5ZWFyIGNvbHVtbi4KIycgQHBhcmFtIHllYXJfY29sMiBUaGUgbmFtZSAoY2hhcikgb3IgaW5kZXggKGludCkgb2YgdGhlIHNlY29uZCB5ZWFyIGNvbHVtbiB0byBiZSBzdWJ0cmFjdGVkIGZyb20uCiMnIEBwYXJhbSB5ZWFyX2NvbDFfbmFtZSBUaGUgbmFtZSBhbGlhcyAoY2hhcikgb2YgdGhlIGZpcnN0IHllYXIgY29sdW1uIHllYXJfY29sMS4KIycgQHBhcmFtIHllYXJfY29sMl9uYW1lIFRoZSBuYW1lIGFsaWFzIChjaGFyKSBvZiB0aGUgc2Vjb25kIHllYXIgY29sdW1uIHllYXJfY29sMi4KIycgQHBhcmFtIHllYXJfbWluIFRoZSBtaW5pbXVtIHllYXIgKGludCkgdG8gY2FsY3VsYXRlIGRpZmZlcmVuY2VzIGZvci4KIycgQHBhcmFtIHllYXJfbWF4IFRoZSBtYXhpbXVtIHllYXIgKGludCkgdG8gY2FsY3VsYXRlIGRpZmZlcmVuY2VzIGZvci4KIycgQHBhcmFtIHRpdGxlIFRoZSB0aXRsZSAoY2hhcikgb2YgdGhlIHBsb3QuCiMnIEBwYXJhbSB0aXRsZV9uIFNldCB0byBUUlVFIHRvIGFkZCB0aGUgbnVtYmVyIG9mIHRvdGFsIHNlZ21lbnRzIGNvbnNpZGVyZWQuCiMnIEBwYXJhbSB4X3RpdGxlIFRoZSB0aXRsZSAoY2hhcikgb2YgdGhlIHgtYXhpcy4KIycgQHBhcmFtIHlfdGl0bGUgVGhlIHRpdGxlIChjaGFyKSBvZiB0aGUgeS1heGlzLgojJyBAcGFyYW0geF9icmVha3MgVGhlIG51bWJlciAoaW50KSBvZiBicmVha3MgdG8gc2hvdyBvbiB0aGUgeC1heGlzLiBTZXQgdG8gRkFMU0UgdG8gbGV0IGdncGxvdCBhdXRvbWF0aWNhbGx5IGRlY2lkZS4KIycgQHBhcmFtIHhfcGVyYyBTZXQgdG8gVFJVRSB0byBzaG93IHByb3BvcnRpb25zIGFuZCBGQUxTRSB0byBzaG93IGNvdW50cy4KIycgQHBhcmFtIG91dF9kYXRhIFNldCB0byBUUlVFIHRvIHJldHVybiBhIGxpc3QKIycgCiMnIEByZXR1cm4gQSBnZ3Bsb3Qgb2YgeWVhcmx5IGRpZmZlcmVuY2VzICh5ZWFyX2NvbDIgLSB5ZWFyX2NvbDEpLCBkaXNwbGF5aW5nIHRoZSBwcm9wb3J0aW9uIG9mIHJvd3MgZm9yIGVhY2ggZGlmZmVyZW5jZSBpbiB5ZWFycy4gSWYgYG91dF9kYXRhYCBpcyBUUlVFIHRoZW4gcmV0dXJucyBhIGxpc3Qgd2l0aCBrZXlzIGBkYXRhYCByZXByZXNlbnRpbmcgdGhlIGRhdGEgdXNlZCBmb3IgcGxvdHRpbmcgYW5kIGBwbG90YCB3aXRoIHRoZSBnZ3Bsb3Qgb2JqZWN0LgojJyBAZXhwb3J0CiMnCnBsb3RfeWVhcmx5X2RpZmYgPC0gZnVuY3Rpb24oCgkJZGYsCgkJeWVhcl9jb2wxID0gImluc3RhbGxfeWVhcl9vcmlnIiwKCQl5ZWFyX2NvbDIgPSAiaW5zdGFsbF95ZWFyIiwKCQl5ZWFyX2NvbDFfbmFtZSA9ICJDaXR5IFllYXIiLAoJCXllYXJfY29sMl9uYW1lID0gIlZlcmlmaWVkIFllYXIiLAoJCXllYXJfbWluID0gc2V0dGluZ3MkeWVhcl9taW4sCgkJeWVhcl9tYXggPSBzZXR0aW5ncyR5ZWFyX21heCwKCQl0aXRsZSA9IHNwcmludGYoCgkJCSJEaWZmZXJlbmNlIGluIFllYXJzLCBDb21wYXJpbmcgJXMgYW5kICVzIiwKCQkJeWVhcl9jb2wxX25hbWUsCgkJCXllYXJfY29sMl9uYW1lCgkJKSwKCQl0aXRsZV9uID0gVFJVRSwKCQl4X3RpdGxlID0gc3ByaW50ZigKCQkJIkRpZmZlcmVuY2UgaW4gWWVhcnMgKCVzIC0gJXMpIiwKCQkJeWVhcl9jb2wyX25hbWUsCgkJCXllYXJfY29sMV9uYW1lCgkJKSwKCQl5X3RpdGxlID0gIlByb3BvcnRpb24gb2YgVG90YWwgU2VnbWVudHMiLAoJCXhfYnJlYWtzID0gMTUsCgkJeF9wZXJjID0gVFJVRSwKCQlvdXRfZGF0YSA9IEZBTFNFCikgewoJCgkjIEZpbHRlciBmb3IgY29tcGFyYWJsZSByb3dzIG9ubHkKCXBkYXRhIDwtIGRmICU+JSBmaWx0ZXIoCgkJIWlzLm5hKC5kYXRhW1t5ZWFyX2NvbDFdXSkgJiAhaXMubmEoLmRhdGFbW3llYXJfY29sMl1dKQoJKQoJCgkjIEZpbHRlciB3aXRoaW4gbWluIHllYXIKCWlmICh5ZWFyX21pbikgewoJCXBkYXRhIDwtIHBkYXRhICU+JSBmaWx0ZXIoCgkJCS5kYXRhW1t5ZWFyX2NvbDFdXSA+PSB5ZWFyX21pbiB8IC5kYXRhW1t5ZWFyX2NvbDJdXSA+PSB5ZWFyX21pbgoJCSkKCX0KCQoJIyBGaWx0ZXIgd2l0aGluIG1heCB5ZWFyCglpZiAoeWVhcl9tYXgpIHsKCQlwZGF0YSA8LSBwZGF0YSAlPiUgZmlsdGVyKAoJCQkuZGF0YVtbeWVhcl9jb2wxXV0gPD0geWVhcl9tYXggfCAuZGF0YVtbeWVhcl9jb2wyXV0gPD0geWVhcl9tYXgKCQkpCgl9CgkKCSMgQWRkIG4gdG8gdGl0bGUKCWlmICh0aXRsZV9uKSB7CgkJdGl0bGUgPC0gc3ByaW50ZigiJXMgKG49JXMpIiwgdGl0bGUsIG5yb3cocGRhdGEpKQoJfQoJCgkjIENhbGMgeWVhcmx5IGRpZmYKCXBkYXRhIDwtIHBkYXRhICU+JQoJCW11dGF0ZSh5ZWFyX2RpZmYgPSBpbnN0YWxsX3llYXIgLSBpbnN0YWxsX3llYXJfb3JpZykgJT4lCgkJY291bnQoeWVhcl9kaWZmKSAlPiUKCQltdXRhdGUobl9wZXJjID0gKG4gLyBzdW0obikpICogMTAwKQoJCgkjIFNldCB0byBwcm9wb3J0aW9ucyBvciBjb3VudHMKCXBkYXRhJHkgPC0gaWYgKHhfcGVyYykgcGRhdGEkbl9wZXJjIGVsc2UgcGRhdGEkbgoJCgkjIFBsb3QgeWVhbHkgZGlmZnMKCW91dCA8LSBwZGF0YSAlPiUgCgkJZ2dwbG90KGFlcygKCQkJeCA9IHllYXJfZGlmZiwKCQkJeSA9IHkKCQkpKSArCgkJZ2VvbV9iYXIoCgkJCXN0YXQgPSAiaWRlbnRpdHkiLAoJCQljb2xvciA9ICIjMzMyYTk0IiwKCQkJZmlsbCA9ICIjYzNkNWU0IiwKCQkJd2lkdGggPSAxCgkJKSArCgkJbGFicygKCQkJdGl0bGUgPSB0aXRsZSwKCQkJeCA9IHhfdGl0bGUsCgkJCXkgPSB5X3RpdGxlCgkJKSArCgkJdGhlbWUoCgkJCXBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKQoJCSkKCQoJIyBBZGQgcGVyY2VudGFnZSBzaWduIGlmIHBlcmNlbnRhZ2VzCglpZiAoeF9wZXJjKSB7CgkJb3V0IDwtIG91dCArCgkJCXNjYWxlX3lfY29udGludW91cygKCQkJCWxhYmVsID0gc2NhbGVzOjpsYWJlbF9udW1iZXIoc3VmZml4ID0gIiUiKQoJCQkpCgl9CgkKCSMgU2V0IHggaW50ZXJ2YWwgYnJlYWtzCglpZiAoeF9icmVha3MpIHsKCQlvdXQgPC0gb3V0ICsgc2NhbGVfeF9jb250aW51b3VzKAoJCQlicmVha3MgPSBzY2FsZXM6OmJyZWFrc19wcmV0dHkoeF9icmVha3MpCgkJKQoJfQoJCgkjIFJldHVybnMgZ2dwbG90IG9iaiBvciBsaXN0CglpZiAob3V0X2RhdGEpIHsKCQlvdXQgPC0gbGlzdCgKCQkJZGF0YSA9IHBkYXRhLAoJCQlwbG90ID0gb3V0CgkJKQoJfQoJcmV0dXJuKG91dCkKfQoKYGBgCgojIERhdGEKCkxvYWQgcmF3IGRhdGEgcHJvdmlkZWQgYnkgS29ucmFkIFNhbXNlbC4KCiMjIFZhbmNvdXZlciBSYXcgRGF0YQoKYGBge3J9CgojIExvYWQgcmF3IGRhdGEKdmFuY19iaWtld2F5cyA8LSByZWFkX2NzdigiLi4vZGF0YS92YW5jb3V2ZXJfYmlrZXdheXNfMjAwOV8yMDIyX3YxLmNzdiIpCnZhbmNfcm9hZHMgPC0gcmVhZF9jc3YoIi4uL2RhdGEvdmFuY291dmVyX3JvYWRzXzIwMDlfMjAyMl92MS5jc3YiKQoKIyBDb21iaW5lIHJhdyBkYXRhCnZhbmMgPC0gdmFuY19iaWtld2F5cyAlPiUKCXNlbGVjdCgKCQlJRF9EQVRBRU5UUlksCgkJSU5TVF9ZUl9PUklHLAoJCUlOU1RfWVIsCgkJSU5TVF9NSU5fSFRZUEUsCgkJVVBHUjFfWVIsCgkJVVBHUjFfTUlOX0hUWVBFLAoJCVVQR1IyX1lSLAoJCVVQR1IyX01JTl9UWVBFLAoJCUFUUl9TRUdNRU5UX0xFTkdUSAoJKSAlPiUKCWxlZnRfam9pbigKCQl2YW5jX3JvYWRzICU+JSBzZWxlY3QoCgkJCUlEX0RBVEFFTlRSWSwKCQkJQVRSX1NFR01FTlRfVFlQRQoJCSksCgkJYnkgPSAiSURfREFUQUVOVFJZIgoJKSAlPiUKCXJlbmFtZSgKCQlpZCA9IElEX0RBVEFFTlRSWSwKCQlpbnN0YWxsX3llYXJfb3JpZyA9IElOU1RfWVJfT1JJRywKCQlpbnN0YWxsX3llYXIgPSBJTlNUX1lSLAoJCWluc3RhbGxfdHlwZSA9IElOU1RfTUlOX0hUWVBFLAoJCXVwZ3JhZGUxX3llYXIgPSBVUEdSMV9ZUiwKCQl1cGdyYWRlMV90eXBlID0gVVBHUjFfTUlOX0hUWVBFLAoJCXVwZ3JhZGUyX3llYXIgPSBVUEdSMl9ZUiwKCQl1cGdyYWRlMl90eXBlID0gVVBHUjJfTUlOX1RZUEUsCgkJc2VnbWVudF9sZW4gPSBBVFJfU0VHTUVOVF9MRU5HVEgsCgkJc2VnbWVudF90eXBlID0gQVRSX1NFR01FTlRfVFlQRQoJKSAlPiUKCW11dGF0ZSgKCQlzZWdtZW50X2xlbiA9IHNlZ21lbnRfbGVuIC8gMTAwMCwKCQlyb2FkX3R5cGUgPSBjYXNlX3doZW4oICMgY3JlYXRlIHJvYWQgdHlwZXMKCSAgICAgICAgc2VnbWVudF90eXBlICVpbiUgYyggIyBhcnRlcmlhbCBlcXVpdgoJICAgICAgICAJIkFydGVyaWFsIgoJICAgICAgICApIH4gIkFydGVyaWFsIiwKCSAgICAgICAgc2VnbWVudF90eXBlICVpbiUgYyggIyBjb2xsZWN0b3IgZXF1aXYKCSAgICAgICAgCSJDb2xsZWN0b3IiLAoJICAgICAgICAJIlNlY29uZGFyeSBBcnRlcmlhbCIsCgkgICAgICAgIAkiU2VjIEFydGVyaWFsIgoJICAgICAgICApIH4gIkNvbGxlY3RvciIsCgkgICAgICAgIHNlZ21lbnRfdHlwZSAlaW4lIGMoICMgbG9jYWwgZXF1aXYKCSAgICAgICAgCSJMYW5lIiwKCSAgICAgICAgCSJSZXNpZGVudGlhbCIsCgkgICAgICAgIAkiTGVhc2VkIiwKCSAgICAgICAgCSJSZWNyZWF0aW9uYWwiCgkgICAgICAgICkgfiAiTG9jYWwiLAoJICAgICAgICAuZGVmYXVsdCA9IHNlZ21lbnRfdHlwZQoJICAgICkKCSkKdmFuYwpgYGAKCiMjIENhbGdhcnkgUmF3IERhdGEKCmBgYHtyfQoKIyBMb2FkIHJhdyBkYXRhCmNhbGdfYmlrZXdheXMgPC0gcmVhZF9jc3YoIi4uL2RhdGEvY2FsZ2FyeV9iaWtld2F5c18yMDA5XzIwMjJfdjEuY3N2IikKY2FsZ19yb2FkcyA8LSByZWFkX2NzdigiLi4vZGF0YS9jYWxnYXJ5X3JvYWRzXzIwMDlfMjAyMl92MS5jc3YiKQoKIyBDb21iaW5lIHJhdyBkYXRhCmNhbGcgPC0gY2FsZ19iaWtld2F5cyAlPiUKICAgIHNlbGVjdCgKCQlTSEFQRV9JRCwKCQlZRUFSX09SSUcsCgkJSU5TVF9ZUiwKCQlJTlNUX01JTl9IVFlQRSwKCQlVUEdSMV9ZUiwKCQlVUEdSMV9NSU5fSFRZUEUsCgkJVVBHUjJfWVIsCgkJVVBHUjJfTUlOX0hUWVBFLAoJCUFUUl9TRUdNRU5UX0xFTkdUSAoJKSAlPiUKCWxlZnRfam9pbigKCQljYWxnX3JvYWRzICU+JSBzZWxlY3QoCgkJCXNoYXBlX2lkLAoJCQljdHBfY2xhc3MKCQkpLAoJCWJ5ID0gam9pbl9ieShTSEFQRV9JRCA9PSBzaGFwZV9pZCkKCSkgJT4lCglyZW5hbWUoCgkJaWQgPSBTSEFQRV9JRCwKCQlpbnN0YWxsX3llYXJfb3JpZyA9IFlFQVJfT1JJRywKCQlpbnN0YWxsX3llYXIgPSBJTlNUX1lSLAoJCWluc3RhbGxfdHlwZSA9IElOU1RfTUlOX0hUWVBFLAoJCXVwZ3JhZGUxX3llYXIgPSBVUEdSMV9ZUiwKCQl1cGdyYWRlMV90eXBlID0gVVBHUjFfTUlOX0hUWVBFLAoJCXVwZ3JhZGUyX3llYXIgPSBVUEdSMl9ZUiwKCQl1cGdyYWRlMl90eXBlID0gVVBHUjJfTUlOX0hUWVBFLAoJCXNlZ21lbnRfbGVuID0gQVRSX1NFR01FTlRfTEVOR1RILAoJCXNlZ21lbnRfdHlwZSA9IGN0cF9jbGFzcwoJKSAlPiUKCW11dGF0ZSgKCQlzZWdtZW50X2xlbiA9IHNlZ21lbnRfbGVuIC8gMTAwMCwKCQlyb2FkX3R5cGUgPSBjYXNlX3doZW4oICMgY3JlYXRlIHJvYWQgdHlwZXMKCSAgICAgICAgc2VnbWVudF90eXBlICVpbiUgYyggIyBhcnRlcmlhbCBlcXVpdgoJICAgICAgICAJIkFydGVyaWFsIFN0cmVldCIsCgkgICAgICAgIAkiSW5kdXN0cmlhbCBBcnRlcmlhbCIsCgkgICAgICAgIAkiTG9jYWwgQXJ0ZXJpYWwiLAoJICAgICAgICAJIlBhcmt3YXkiLAoJICAgICAgICAJIlVyYmFuIEJvdWxldmFyZCIKCSAgICAgICAgKSB+ICJBcnRlcmlhbCIsCgkgICAgICAgIHNlZ21lbnRfdHlwZSAlaW4lIGMoICMgY29sbGVjdG9yIGVxdWl2CgkgICAgICAgIAkiTmVpZ2hib3VyaG9vZCBCb3VsZXZhcmQiLAoJICAgICAgICAJIkNvbGxlY3RvciIsCgkgICAgICAgIAkiUHJpbWFyeSBDb2xsZWN0b3IiLAoJICAgICAgICAJIlNrZWxldGFsIFJvYWQiCgkgICAgICAgICkgfiAiQ29sbGVjdG9yIiwKCSAgICAgICAgc2VnbWVudF90eXBlICVpbiUgYyggIyBsb2NhbCBlcXVpdgoJICAgICAgICAJIkFjY2VzcyBSb3V0ZSIsCgkgICAgICAgIAkiUmVzaWRlbnRpYWwgU3RyZWV0IiwKCSAgICAgICAgCSJBY3Rpdml0eSBDZW50ZXIgU3RyZWV0IiwKCSAgICAgICAgCSJIaXN0b3JpYyBSb2FkIEFsbG93YW5jZSIsCgkgICAgICAgIAkiTGFuZXMgKEFsbGV5cykiLAoJICAgICAgICAJIkluZHVzdHJpYWwgU3RyZWV0IgoJICAgICAgICApIH4gIkxvY2FsIiwKCSAgICAgICAgLmRlZmF1bHQgPSBzZWdtZW50X3R5cGUKCSAgICApCgkpCmNhbGcKYGBgCgojIyBUb3JvbnRvIFJhdyBEYXRhIHsudGFic2V0fQoKYGBge3J9CnRvcm9uX3JhdyA8LSByZWFkX3NmKCIuLi9kYXRhL3Jhdy9Ub3JvbnRvIEFTIEV4cG9ydC9Ub3JvbnRvX0FTXzEzMjMuc2hwIikKYGBgCgojIyMgTWFwCgpgYGB7ciBvdXQud2lkdGggPSAiMTAwJSJ9CnRtYXBfbW9kZSgidmlldyIpCnRtX3NoYXBlKHRvcm9uX3JhdykgKwoJdG1fbGluZXMoY29sID0gIklOU1RfVE1JTiIsIHBvcHVwLnZhcnMgPSBUUlVFKQpgYGAKCiMjIyBEYXRhCgpgYGB7cn0KdG9yb25fcmF3ICU+JSBhc190aWJibGUKYGBgCgojIyMgRGV0YWlscwoKYGBge3J9CnByaW50KHRvcm9uX3JhdykKYGBgCgojIFByZXByb2Nlc3NpbmcKCiMjIFRvcm9udG8gUHJlcHJvY2Vzc2VkIERhdGEKCk5vdGUgMTogV29ya2luZyBvbiBnZXR0aW5nIGNvbXBsZXRlIGRhdGEuCgpgYGAKIyBQcmVwcm9jZXNzIGRhdGEKdG9yb25fcHJlcCA8LSB0b3Jvbl9yYXcgJT4lCglzZWxlY3QoICMgc2VsZWN0IGFuZCByZW5hbWUKCQlpZCA9IE9CSkVDVEkyLAoJCXN0cmVldCA9IFNUUkVFVF83LAoJCXN0cmVldF9mcm9tID0gRlJPTV9TVDgsCgkJc3RyZWV0X3RvID0gVE9fU1RSRTksCgkJaW5zdGFsbF95ZWFyID0gSU5TVEFMTDQsCgkJaW5zdGFsbF90eXBlID0gSU5GUkFfSDIwLCAjIHNpbWlsYXIgdG8gQ19JTkZSQV9ICgkJdmVyaWZ5X2luc3RhbGxfeWVhciA9IElOU1RfWVIsCgkJdmVyaWZ5X2luc3RhbGxfZGF0ZSA9IElOU1RfREFURSwKCQl2ZXJpZnlfaW5zdGFsbF90eXBlID0gSU5TVF9UTUlOLAoJCXZlcmlmeV9pbnN0YWxsX2NvbW1lbnQgPSBJTlNUX0NPTU0sCgkJdmVyaWZ5X3VwZ3JhZGUxX3llYXIgPSBVUEdSMV9ZUiwKCQl2ZXJpZnlfdXBncmFkZTFfZGF0ZSA9IFVQR1IxX0RBVEUsCgkJdmVyaWZ5X3VwZ3JhZGUxX3R5cGUgPSBVUEdSMV9UTUlOLAoJCXZlcmlmeV91cGdyYWRlMV9jb21tZW50ID0gVVBHUjFfQ09NTSwKCQl2ZXJpZnlfdXBncmFkZTJfeWVhciA9IFVQR1IyX1lSLAoJCXZlcmlmeV91cGdyYWRlMl9kYXRlID0gVVBHUjJfREFURSwKCQl2ZXJpZnlfdXBncmFkZTJfdHlwZSA9IFVQR1IyX1RNSU4sCgkJdmVyaWZ5X3VwZ3JhZGUyX2NvbW1lbnQgPSBVUEdSMl9DT01NLAoJCXZlcmlmeV9leGNsX2ZsYWcgPSBFWENMX0ZMQUcKCSkgJT4lCgltdXRhdGUoICMgZGF0YSB0eXBlcwoJCWlkID0gYXMuY2hhcmFjdGVyKGlkKSwKCQlzdHJlZXQgPSBhcy5jaGFyYWN0ZXIoc3RyZWV0KSwKCQlzdHJlZXRfZnJvbSA9IGFzLmNoYXJhY3RlcihzdHJlZXRfZnJvbSksCgkJc3RyZWV0X3RvID0gYXMuY2hhcmFjdGVyKHN0cmVldF90byksCgkJaW5zdGFsbF95ZWFyID0gYXMubnVtZXJpYyhpbnN0YWxsX3llYXIpLAoJCWluc3RhbGxfdHlwZSA9IGFzLmNoYXJhY3RlcihpbnN0YWxsX3R5cGUpLAoJCXZlcmlmeV9pbnN0YWxsX3llYXIgPSBhcy5udW1lcmljKHZlcmlmeV9pbnN0YWxsX3llYXIpLAoJCXZlcmlmeV9pbnN0YWxsX2RhdGUgPSBhcy5jaGFyYWN0ZXIodmVyaWZ5X2luc3RhbGxfZGF0ZSksCgkJdmVyaWZ5X2luc3RhbGxfdHlwZSA9IGFzLmNoYXJhY3Rlcih2ZXJpZnlfaW5zdGFsbF90eXBlKSwKCQl2ZXJpZnlfaW5zdGFsbF9jb21tZW50ID0gYXMuY2hhcmFjdGVyKHZlcmlmeV9pbnN0YWxsX2NvbW1lbnQpLAoJCXZlcmlmeV91cGdyYWRlMV95ZWFyID0gYXMubnVtZXJpYyh2ZXJpZnlfdXBncmFkZTFfeWVhciksCgkJdmVyaWZ5X3VwZ3JhZGUxX2RhdGUgPSBhcy5jaGFyYWN0ZXIodmVyaWZ5X3VwZ3JhZGUxX2RhdGUpLAoJCXZlcmlmeV91cGdyYWRlMV90eXBlID0gYXMuY2hhcmFjdGVyKHZlcmlmeV91cGdyYWRlMV90eXBlKSwKCQl2ZXJpZnlfdXBncmFkZTFfY29tbWVudCA9IGFzLmNoYXJhY3Rlcih2ZXJpZnlfdXBncmFkZTFfY29tbWVudCksCgkJdmVyaWZ5X3VwZ3JhZGUyX3llYXIgPSBhcy5udW1lcmljKHZlcmlmeV91cGdyYWRlMl95ZWFyKSwKCQl2ZXJpZnlfdXBncmFkZTJfZGF0ZSA9IGFzLmNoYXJhY3Rlcih2ZXJpZnlfdXBncmFkZTJfZGF0ZSksCgkJdmVyaWZ5X3VwZ3JhZGUyX3R5cGUgPSBhcy5jaGFyYWN0ZXIodmVyaWZ5X3VwZ3JhZGUyX3R5cGUpLAoJCXZlcmlmeV91cGdyYWRlMl9jb21tZW50ID0gYXMuY2hhcmFjdGVyKHZlcmlmeV91cGdyYWRlMl9jb21tZW50KSwKCQl2ZXJpZnlfZXhjbF9mbGFnID0gYXMuY2hhcmFjdGVyKHZlcmlmeV9leGNsX2ZsYWcpCgkpICU+JQoJbXV0YXRlKCAjIGNsZWFuIHZhbHVlcwoJCWluc3RhbGxfeWVhciA9IG5hX2lmKGluc3RhbGxfeWVhciwgMCksCgkJdmVyaWZ5X2luc3RhbGxfeWVhciA9IG5hX2lmKHZlcmlmeV9pbnN0YWxsX3llYXIsIDApLAoJCXZlcmlmeV9pbnN0YWxsX2RhdGUgPSBuYV9pZih2ZXJpZnlfaW5zdGFsbF9kYXRlLCAiTkEiKSwKCQl2ZXJpZnlfaW5zdGFsbF90eXBlID0gbmFfaWYodmVyaWZ5X2luc3RhbGxfdHlwZSwgIk5BIikgJT4lCgkJCXN0cl9yZXBsYWNlX2FsbCgiW15bOmFscGhhOl1dfFxccyIsICIiKSwKCQl2ZXJpZnlfaW5zdGFsbF9jb21tZW50ID0gbmFfaWYodmVyaWZ5X2luc3RhbGxfY29tbWVudCwgIk5BIiksCgkJdmVyaWZ5X3VwZ3JhZGUxX3llYXIgPSBuYV9pZih2ZXJpZnlfdXBncmFkZTFfeWVhciwgMCksCgkJdmVyaWZ5X3VwZ3JhZGUxX2RhdGUgPSBuYV9pZih2ZXJpZnlfdXBncmFkZTFfZGF0ZSwgIk5BIiksCgkJdmVyaWZ5X3VwZ3JhZGUxX3R5cGUgPSBuYV9pZih2ZXJpZnlfdXBncmFkZTFfdHlwZSwgIk5BIikgJT4lCgkJCXN0cl9yZXBsYWNlX2FsbCgiW15bOmFscGhhOl1dfFxccyIsICIiKSwKCQl2ZXJpZnlfdXBncmFkZTFfY29tbWVudCA9IG5hX2lmKHZlcmlmeV91cGdyYWRlMV9jb21tZW50LCAiTkEiKSwKCQl2ZXJpZnlfdXBncmFkZTJfeWVhciA9IG5hX2lmKHZlcmlmeV91cGdyYWRlMl95ZWFyLCAwKSwKCQl2ZXJpZnlfdXBncmFkZTJfZGF0ZSA9IG5hX2lmKHZlcmlmeV91cGdyYWRlMl9kYXRlLCAiTkEiKSwKCQl2ZXJpZnlfdXBncmFkZTJfdHlwZSA9IG5hX2lmKHZlcmlmeV91cGdyYWRlMl90eXBlLCAiTkEiKSAlPiUKCQkJc3RyX3JlcGxhY2VfYWxsKCJbXls6YWxwaGE6XV18XFxzIiwgIiIpLAoJCXZlcmlmeV91cGdyYWRlMl9jb21tZW50ID0gbmFfaWYodmVyaWZ5X3VwZ3JhZGUyX2NvbW1lbnQsICJOQSIpLAoJCXZlcmlmeV9leGNsX2ZsYWcgPSAgbmFfaWYodmVyaWZ5X2V4Y2xfZmxhZywgIk5BIikgJT4lCgkJCXN0cl9yZXBsYWNlX2FsbCgiXFxzIiwgIiIpCgkpICU+JQoJbXV0YXRlKCAjIGNyZWF0ZSBjb2x1bW5zCgkJbGVuX2ttID0gYXMubnVtZXJpYyhzdF9sZW5ndGgoZ2VvbWV0cnkpKSAvIDEwMDAsCgkJLmJlZm9yZSA9IGdlb21ldHJ5CgkpCgojIFNhdmUgZ2VvanNvbgp0b3Jvbl9wcmVwICU+JQoJc3Rfd3JpdGUoIi4uL2RhdGEvdG9yb250b19iaWtld2F5c192MS5nZW9qc29uIiwgZGVsZXRlX2RzbiA9IFRSVUUpCgojIFNhdmUgY3N2CiMgc3RfcmVhZCgiLi4vZGF0YS90b3JvbnRvX2Jpa2V3YXlzX3YxLmNzdiIsIG9wdGlvbnMgPSAiR0VPTV9QT1NTSUJMRV9OQU1FUz1nZW9tZXRyeSIsIGNycyA9ICJ1cm46b2djOmRlZjpjcnM6T0dDOjEuMzpDUlM4NCIpCnRvcm9uX3ByZXAgJT4lCgltdXRhdGUoCgkJZ2VvbWV0cnkgPSBzdF9hc190ZXh0KGdlb21ldHJ5KSwKCQlnZW9tZXRyeV9jcnMgPSBzdF9jcnModG9yb25fcHJlcCkkcHJvajRzdHJpbmcsCgkJLmJlZm9yZSA9IGdlb21ldHJ5CgkpICU+JQoJd3JpdGVfY3N2KCIuLi9kYXRhL3Rvcm9udG9fYmlrZXdheXNfdjEuY3N2IiwgbmEgPSAiIikKdG9yb25fcHJlcApgYGAKCk5vdGUgMjogVXNpbmcgdGhpcyBvbmUgZm9yIG5vdy4KCmBgYHtyfQoKIyBMb2FkIHJhdyBkYXRhCnRvcm9uX2Jpa2V3YXlzIDwtIHJlYWRfY3N2KCIuLi9kYXRhL3Rvcm9udG9fYmlrZXdheXNfMjAwOV8yMDIyX3YxLmNzdiIpCnRvcm9uX3JvYWRzIDwtIHJlYWRfY3N2KCIuLi9kYXRhL3Rvcm9udG9fcm9hZHNfMjAwOV8yMDIyX3YxLmNzdiIpCgojIENvbWJpbmUgcmF3IGRhdGEKdG9yb24gPC0gdG9yb25fYmlrZXdheXMgJT4lCiAgICBzZWxlY3QoCgkJSURfT0lELAoJCUlOU1RBTExFRF9PUklHLAoJCUlOU1RfWVIsCgkJSU5TVF9NSU5fSFRZUEUsCgkJVVBHUjFfWVIsCgkJVVBHUjFfTUlOX0hUWVBFLAoJCVVQR1IyX1lSLAoJCVVQR1IyX01JTl9IVFlQRSwKCQlBVFJfU0VHTUVOVF9MRU5HVEgKICAgICkgJT4lCglsZWZ0X2pvaW4oCgkJdG9yb25fcm9hZHMgJT4lIHNlbGVjdCgKCQkJT0lEXywKCQkJRkVBVFVSRTM2CgkJKSwKCQlieSA9IGpvaW5fYnkoSURfT0lEID09IE9JRF8pCgkpICU+JQoJcmVuYW1lKAoJCWlkID0gSURfT0lELAoJCWluc3RhbGxfeWVhcl9vcmlnID0gSU5TVEFMTEVEX09SSUcsCgkJaW5zdGFsbF95ZWFyID0gSU5TVF9ZUiwKCQlpbnN0YWxsX3R5cGUgPSBJTlNUX01JTl9IVFlQRSwKCQl1cGdyYWRlMV95ZWFyID0gVVBHUjFfWVIsCgkJdXBncmFkZTFfdHlwZSA9IFVQR1IxX01JTl9IVFlQRSwKCQl1cGdyYWRlMl95ZWFyID0gVVBHUjJfWVIsCgkJdXBncmFkZTJfdHlwZSA9IFVQR1IyX01JTl9IVFlQRSwKCQlzZWdtZW50X2xlbiA9IEFUUl9TRUdNRU5UX0xFTkdUSCwKCQlzZWdtZW50X3R5cGUgPSBGRUFUVVJFMzYKCSkgJT4lCgltdXRhdGUoCgkJc2VnbWVudF9sZW4gPSBzZWdtZW50X2xlbiAvIDEwMDAsCgkJcm9hZF90eXBlID0gY2FzZV93aGVuKCAjIGNyZWF0ZSByb2FkIHR5cGVzCgkgICAgICAgIHNlZ21lbnRfdHlwZSAlaW4lIGMoICMgYXJ0ZXJpYWwgZXF1aXYKCSAgICAgICAgCSJNYWpvciBBcnRlcmlhbCIsCgkgICAgICAgIAkiTWFqb3IgQXJ0ZXJpYWwgUmFtcCIsCgkgICAgICAgIAkiTWlub3IgQXJ0ZXJpYWwiCgkgICAgICAgICkgfiAiQXJ0ZXJpYWwiLAoJICAgICAgICBzZWdtZW50X3R5cGUgJWluJSBjKCAjIGNvbGxlY3RvciBlcXVpdgoJICAgICAgICAJIkNvbGxlY3RvciIKCSAgICAgICAgKSB+ICJDb2xsZWN0b3IiLAoJICAgICAgICBzZWdtZW50X3R5cGUgJWluJSBjKCAgIyBsb2NhbCBlcXVpdgoJICAgICAgICAJIkxvY2FsIiwKCSAgICAgICAgCSJPdGhlciIKCSAgICAgICAgKSB+ICJMb2NhbCIsCgkgICAgICAgIC5kZWZhdWx0ID0gc2VnbWVudF90eXBlCgkgICAgKQoJKQp0b3JvbgpgYGAKCiMgRmlndXJlcwoKIyMgRmlndXJlIDE6IEZsb3cgZGlhZ3JhbSBvZiBpbmNsdXNpb24gY3JpdGVyaWEgZm9yIGJpa2V3YXkgc2VnbWVudHMgaW4gVmFuY291dmVyLCBDYWxnYXJ5LCBhbmQgVG9yb250by4KClRoaXMgZmxvd2NoYXJ0IHByb3ZpZGVzIGEgaGlnaC1sZXZlbCBvdmVydmlldyBvZiB0aGUgc2VnbWVudCBpbmNsdXNpb25zIGFuZCBleGNsdXNpb25zIGZvciBlYWNoIG11bmljaXBhbGl0eS4gRGF0YSBmcm9tIENhbGdhcnkgd2VyZSBzcGVjaWZpYyB0byBvbi1zdHJlZXQgcm91dGVzIG9ubHkuIEZvciBkZXRhaWxlZCBmbG93IGRpYWdyYW1zIHNwZWNpZmljIHRvIGVhY2ggbXVuaWNpcGFsaXR5LCBwbGVhc2UgcmVmZXIgdG8gdGhlIEFwcGVuZGl4LgoKYGBge3J9CmdyVml6KCIKZGlncmFwaCB7CglyYW5rZGlyID0gTFIKCW5vZGVbCgkJc2hhcGUgPSBib3gsCgkJd2lkdGggPSAyLjc1LAoJCWhlaWdodCA9IDEuMzUsCgkJc3R5bGUgPSBmaWxsZWQsCgkJZmlsbGNvbG9yID0gd2hpdGUsCgkJcGVud2lkdGggPSAxLjUsCgkJZm9udG5hbWUgPSAnQXJpYWwnCgldCglsYXlvdXQgPSBuZWF0bwoJCglmaWx0ZXIxWwoJCWxhYmVsID0gJ0ZpbHRlcmluZycsCgkJaGVpZ2h0ID0gMC4yNSwKCQlzaGFwZSA9IHBsYWludGV4dCwKCQlzdHlsZT0nJywgcG9zID0gJzEuNiwxLjQyNSEnCgldCglmaWx0ZXIyWwoJCXN0eWxlID0gaW52aXMsCgkJcG9zID0gJzEuNiwtMy45IScKCV0KCWZpbHRlcjEgLT4gZmlsdGVyMiBbc3R5bGUgPSBkYXNoZWQsIGRpciA9IG5vbmUsIGNvbG9yID0gJyNiMGIwYjAnXQoJCglzY3JlZW4xWwoJCWxhYmVsID0gJ1NjcmVlbmluZycsCgkJaGVpZ2h0ID0gMC4yNSwKCQlzaGFwZSA9IHBsYWludGV4dCwKCQlzdHlsZT0nJywgcG9zID0gJzQuODUsMS40MjUhJwoJXQoJc2NyZWVuMlsKCQlzdHlsZSA9IGludmlzLAoJCXBvcyA9ICc0Ljg1LC0zLjkhJwoJXQoJc2NyZWVuMSAtPiBzY3JlZW4yIFtzdHlsZSA9IGRhc2hlZCwgZGlyID0gbm9uZSwgY29sb3IgPSAnI2IwYjBiMCddCgkKCW9wZW5fZGF0YVsKCQlsYWJlbCA9ICdPcGVuIERhdGEnLAoJCWhlaWdodCA9IDAuNSwKCQlmaWxsY29sb3IgPSAnI2Q3ZTlmZScsCgkJcG9zID0gJzAsMSEnCgldCgllbGlnX2RhdGFbCgkJbGFiZWwgPSAnRWxpZ2libGUgU2VnbWVudHMnLAoJCWhlaWdodCA9IDAuNSwKCQlmaWxsY29sb3IgPSAnI2Q3ZTlmZScsCgkJcG9zID0gJzMuMjUsMSEnCgldCglpbmNsX2RhdGFbCgkJbGFiZWwgPSAnSW5jbHVzaW9ucycsCgkJaGVpZ2h0ID0gMC41LAoJCWZpbGxjb2xvciA9ICcjZDdlOWZlJywKCQlwb3MgPSAnNi41LDEhJwoJXQoJCglvcGVuX3ZhbmNbCgkJbGFiZWwgPSA8PGI+VmFuY291dmVyPC9iPjxici8+TiA9IEBAMS0xIFNlZ21lbnRzPGJyLz48aT4oQEAyLTEpPGJyLz5Eb3dubG9hZGVkOiBKYW51YXJ5IDIwMjM8L2k+PiwKCQlwb3MgPSAnMCwtMC4wNiEnCgldCglvcGVuX2NhbGdbCgkJbGFiZWwgPSA8PGI+Q2FsZ2FyeTwvYj48YnIvPk4gPSBAQDEtMiBTZWdtZW50czxici8+PGk+KEBAMi0yKTxici8+RG93bmxvYWRlZDogSmFudWFyeSAyMDIzPC9pPj4sCgkJcG9zID0gJzAsLTEuNjUhJwoJXQoJb3Blbl90b3JvblsKCQlsYWJlbCA9IDw8Yj5Ub3JvbnRvPC9iPjxici8+TiA9IEBAMS0zIFNlZ21lbnRzPGJyLz48aT4oQEAyLTMpPGJyLz5Eb3dubG9hZGVkOiBKYW51YXJ5IDIwMjM8L2k+PiwgCgkJcG9zID0gJzAsLTMuMjQhJwoJXQoJCgllbGlnX3ZhbmNbCgkJbGFiZWwgPSA8biA9IEBAMy0xIFNlZ21lbnRzPGJyLz4oQEA0LTEpPGJyLz48aT48Yj5FeGNsdXNpb25zPC9iPkBANS0xQEA2LTFAQDctMTwvaT4+LAoJCXBvcyA9ICczLjI1LC0wLjA2IScKCV0KCQoJZWxpZ19jYWxnWwoJCWxhYmVsID0gPG4gPSBAQDMtMiBTZWdtZW50czxici8+PGk+KEBANC0yKTxici8+PGI+RXhjbHVzaW9uczwvYj5AQDUtMkBANi0yQEA3LTI8L2k+PiwKCQlwb3MgPSAnMy4yNSwtMS42NSEnCgldCgllbGlnX3Rvcm9uWwoJCWxhYmVsID0gPG4gPSBAQDMtMyBTZWdtZW50czxici8+PGk+KEBANC0zKTxici8+PGI+RXhjbHVzaW9uczwvYj5AQDUtM0BANi0zQEA3LTM8L2k+PiwKCQlwb3MgPSAnMy4yNSwtMy4yNCEnCgldCgkKCWluY2xfdmFuY1sKCQlsYWJlbCA9IDxuID0gQEA4LTEgU2VnbWVudHM8YnIvPjxpPihAQDktMSk8YnIvPjxiPkV4Y2x1c2lvbnM8L2I+QEAxMC0xQEAxMS0xPC9pPj4sCgkJcG9zID0gJzYuNSwtMC4wNiEnCgldCglpbmNsX2NhbGdbCgkJbGFiZWwgPSA8biA9IEBAOC0yIFNlZ21lbnRzPGJyLz48aT4oQEA5LTIpPGJyLz48Yj5FeGNsdXNpb25zPC9iPkBAMTAtMkBAMTEtMjwvaT4+LAoJCXBvcyA9ICc2LjUsLTEuNjUhJwoJXQoJaW5jbF90b3JvblsKCQlsYWJlbCA9IDxuID0gQEA4LTMgU2VnbWVudHM8YnIvPjxpPihAQDktMyk8YnIvPjxiPkV4Y2x1c2lvbnM8L2I+QEAxMC0zQEAxMS0zPC9pPj4sCgkJcG9zID0gJzYuNSwtMy4yNCEnCgldCgkKCW5vdGVbCgkJbGFiZWw9PDxpPkBAMTI8L2k+PiwKCQlzdHlsZSA9ICcnLAoJCXNoYXBlID0gcGxhaW50ZXh0LAoJCWZvbnRzaXplID0gMTIsCgkJcG9zID0gJzMuMjUsLTQuMTUhJwoJXQoJCglvcGVuX3ZhbmMgLT4gZWxpZ192YW5jIC0+IGluY2xfdmFuYwoJb3Blbl9jYWxnIC0+IGVsaWdfY2FsZyAtPiBpbmNsX2NhbGcKCW9wZW5fdG9yb24gLT4gZWxpZ190b3JvbiAtPiBpbmNsX3Rvcm9uCn0KClsxXTogYygnMzY2NCcsICc0MTY2JywgJzEzMjMnKSAjIG9wZW4gc2VnbWVudHMKWzJdOiBjKCczNDIuMSBrbScsICc1NjkuNyBrbScsICc3NTUuOCBrbScpICMgb3BlbiBrbQpbM106IGMoJzc4MCcsICc3ODInLCAnMzMxJykgIyBlbGlnaWJsZSBzZWdtZW50cwpbNF06IGMoJzcxLjIga20nLCAnODguNyBrbScsICcyMDUuNCBrbScpICMgZWxpZ2libGUga20KWzVdOiBjKCc8YnIvPkluZWxpZ2libGU6IG49Mjg4MycsICc8YnIvPkluZWxpZ2libGU6IG49MzM4MycsICc8YnIvPkluZWxpZ2libGU6IG49OTkyJykgIyBlbGlnaWJsZSBleGNsdXNpb25zCls2XTogYygnPGJyLz5EdXBsaWNhdGVzIG49MScsICcnLCAnJykgIyBlbGlnYmxlIGR1cGxpY2F0ZXMKWzddOiBjKCcnLCAnPGJyLz5ObyBQb2x5bGluZSBEYXRhOiBuPTEnLCAnJykgIyBlbGlnaWJsZSBwb2x5bGluZSBkYXRhCls4XTogYygnNzQ1JywgJzc1MCcsICczMjYnKSAjIGluY2x1c2lvbiBzZWdtZW50cwpbOV06IGMoJzY5Ljkga20nLCAnODUga20nLCAnMjAzLjUga20nKSAjIGluY2x1c2lvbiBrbQpbMTBdOiBjKCc8YnIvPipNaXNjbGFzc2lmaWNhdGlvbnM6IG49MzQnLCAnPGJyLz4qTWlzY2xhc3NpZmljYXRpb25zOiBuPTMyJywgJzxici8+Kk1pc2NsYXNzaWZpY2F0aW9uczogbj01JykgIyBpbmNsdXNpb24gbWlzY2xhc3NpZmljYXRpb25zClsxMV06IGMoJzxici8+RHVwbGljYXRlczogbj0xJywgJycsICcnKSAjIGluY2x1c2lvbiBkdXBsaWNhdGVzClsxMl06ICcqRGVub3RlcyBzZWdtZW50cyBtaXNjbGFzc2lmaWVkIGFzIGFuIGluZWxpZ2libGUgdHlwZSAob2ZmLXN0cmVldCBwYXRoLCBzaGFyZWQgcm9hZCwgb3IgaW5hY3RpdmUgdGVtcG9yYXJ5IGluZnJhc3RydWN0dXJlKScKIikKYGBgCgpgYGAgICAgICAgICAKJT4lCiAgICBleHBvcnRfc3ZnICU+JQogICAgY2hhclRvUmF3ICU+JQogICAgcnN2Z19wbmcoInRlc3QucG5nIikKYGBgCgojIyBGaWd1cmUgMjogQ2hhbmdlcyBpbiBkZWRpY2F0ZWQgY3ljbGluZyBpbmZyYXN0cnVjdHVyZSBiZXR3ZWVuIDIwMDkgYW5kIDIwMjIgZm9yIFZhbmNvdXZlciwgQ2FsZ2FyeSwgYW5kIFRvcm9udG8gYnkgaW5mcmFzdHJ1Y3R1cmUgY2F0ZWdvcnkuCgpBc3Nlc3NlZCB1c2luZyByb2Fkd2F5IGNlbnRyZWxpbmUta20sIHdpdGggaW5mcmFzdHJ1Y3R1cmUgY2xhc3NpZmljYXRpb25zIGRldGVybWluZWQgYnkgdGhlIG1vc3QgcHJvdGVjdGl2ZSBlbGVtZW50IHByZXNlbnQgYWxvbmcgZWFjaCByb2FkIHNlZ21lbnQuCgpgYGB7ciBmaWcud2lkdGg9OC41LCBmaWcuaGVpZ2h0PTExfQpwbG90X3llYXJseV9sZW5faW5mcmEobGlzdCgKCSJWYW5jb3V2ZXIsIENBIiA9IHZhbmMsCgkiQ2FsZ2FyeSwgQ0EiID0gY2FsZywKCSJUb3JvbnRvLCBDQSIgPSB0b3JvbgopKQpgYGAKCiMjIEZpZ3VyZSA0OiBDaGFuZ2VzIGluIERlZGljYXRlZCBPbi1TdHJlZXQgSW5mcmFzdHJ1Y3R1cmUgU2luY2UgSmFudWFyeSAyMDIwIGZvciBWYW5jb3V2ZXIsIENhbGdhcnksIGFuZCBUb3JvbnRvLgoKTmV3IGluc3RhbGxhdGlvbnMgb2YgZGVkaWNhdGVkIGluZnJhc3RydWN0dXJlIGFyZSBkZW5vdGVkIGluIGdyZWVuLCB1cGdyYWRlcyBmcm9tIGEgcHJldmlvdXMgZGVkaWNhdGVkIGluZnJhc3RydWN0dXJlIHR5cGUgYXJlIGRlbm90ZWQgaW4gb3JhbmdlLiBNYXBwZWQgaW4gQXJjR0lTIFBybyAzLjAuMS4KCmBgYHtyfQpgYGAKCiMgQXBwZW5kaXggMSAtIFN1cHBsZW1lbnRhcnkgUmVzdWx0cwoKIyMgU3VwcGxlbWVudGFyeSBGaWd1cmUgNDogQ2hhbmdlcyBpbiBkZWRpY2F0ZWQgY3ljbGluZyBpbmZyYXN0cnVjdHVyZSBiZXR3ZWVuIDIwMDkgYW5kIDIwMjEgZm9yIHRoZSBNdW5pY2lwYWxpdHkgb2YgVmFuY291dmVyLCBDQS4KCkJ5IChBKSByb2Fkd2F5IGNsYXNzaWZpY2F0aW9uLCBhbmQgKEIpIGluZnJhc3RydWN0dXJlIGRpc3RyaWJ1dGlvbiB3aXRoaW4gZWFjaCByb2FkIGNsYXNzLiBBc3Nlc3NlZCB1c2luZyByb2Fkd2F5IGNlbnRyZWxpbmUta20sIHdpdGggaW5mcmFzdHJ1Y3R1cmUgY2xhc3NpZmljYXRpb24gZGV0ZXJtaW5lZCBieSB0aGUgbW9zdCBwcm90ZWN0aXZlIGVsZW1lbnQgcHJlc2VudCBhbG9uZyBlYWNoIHJvYWQgc2VnbWVudC4KCmBgYHtyIGZpZy53aWR0aD0xOCwgZmlnLmhlaWdodD0xMH0KcGxvdF95ZWFybHlfbGVuX3JvYWQoCgl2YW5jLAoJdGl0bGUgPSAiUm9hZHdheXMgd2l0aCBEZWRpY2F0ZWQgQ3ljbGluZyBJbmZyYXN0cnVjdHVyZSAoVmFuY291dmVyLCBDQSkiCikKYGBgCgojIyBTdXBwbGVtZW50YXJ5IEZpZ3VyZSA1OiBDaGFuZ2VzIGluIGRlZGljYXRlZCBjeWNsaW5nIGluZnJhc3RydWN0dXJlIGJldHdlZW4gMjAwOSBhbmQgMjAyMiBmb3IgdGhlIE11bmljaXBhbGl0eSBvZiBDYWxnYXJ5LCBDQS4KCkJ5IChBKSByb2Fkd2F5IGNsYXNzaWZpY2F0aW9uLCBhbmQgKEIpIGluZnJhc3RydWN0dXJlIGRpc3RyaWJ1dGlvbiB3aXRoaW4gZWFjaCByb2FkIGNsYXNzLiBBc3Nlc3NlZCB1c2luZyByb2Fkd2F5IGNlbnRyZWxpbmUta20sIHdpdGggaW5mcmFzdHJ1Y3R1cmUgY2xhc3NpZmljYXRpb24gZGV0ZXJtaW5lZCBieSB0aGUgbW9zdCBwcm90ZWN0aXZlIGVsZW1lbnQgcHJlc2VudCBhbG9uZyBlYWNoIHJvYWQgc2VnbWVudC4KCmBgYHtyIGZpZy53aWR0aD0xOCwgZmlnLmhlaWdodD0xMH0KcGxvdF95ZWFybHlfbGVuX3JvYWQoCgljYWxnLAoJdGl0bGUgPSAiUm9hZHdheXMgd2l0aCBEZWRpY2F0ZWQgQ3ljbGluZyBJbmZyYXN0cnVjdHVyZSAoQ2FsZ2FyeSwgQ0EpIgopCmBgYAoKIyMgU3VwcGxlbWVudGFyeSBGaWd1cmUgNjogQ2hhbmdlcyBpbiBkZWRpY2F0ZWQgY3ljbGluZyBpbmZyYXN0cnVjdHVyZSBiZXR3ZWVuIDIwMDkgYW5kIDIwMjIgZm9yIHRoZSBNdW5pY2lwYWxpdHkgb2YgVG9yb250bywgQ0EuCgpCeSAoQSkgcm9hZHdheSBjbGFzc2lmaWNhdGlvbiwgYW5kIChCKSBpbmZyYXN0cnVjdHVyZSBkaXN0cmlidXRpb24gd2l0aGluIGVhY2ggcm9hZCBjbGFzcy4gQXNzZXNzZWQgdXNpbmcgcm9hZHdheSBjZW50cmVsaW5lLWttLCB3aXRoIGluZnJhc3RydWN0dXJlIGNsYXNzaWZpY2F0aW9uIGRldGVybWluZWQgYnkgdGhlIG1vc3QgcHJvdGVjdGl2ZSBlbGVtZW50IHByZXNlbnQgYWxvbmcgZWFjaCByb2FkIHNlZ21lbnQuCgpgYGB7ciBmaWcud2lkdGg9MTgsIGZpZy5oZWlnaHQ9MTB9CnBsb3RfeWVhcmx5X2xlbl9yb2FkKAoJdG9yb24sCgl0aXRsZSA9ICJSb2Fkd2F5cyB3aXRoIERlZGljYXRlZCBDeWNsaW5nIEluZnJhc3RydWN0dXJlIChUb3JvbnRvLCBDQSkiCikKYGBgCgojIyBTdXBwbGVtZW50YXJ5IEZpZ3VyZSA3OiBBIGNvbXBhcmF0aXZlIGFuYWx5c2lzIGJldHdlZW4gbXVuaWNpcGFsIGRhdGEgYW5kIHZlcmlmaWVkIGRhdGEgb24gdGhlIGluc3RhbGxhdGlvbiB5ZWFycyBmb3IgY3ljbGluZyBpbmZyYXN0cnVjdHVyZSBpbiBWYW5jb3V2ZXIsIENBLiB7LnRhYnNldH0KCmBgYHtyfQoKIyBDcmVhdGUgdGhlIHBsb3QKc2ZpZzcgPC0gcGxvdF95ZWFybHlfZGlmZigKCXZhbmMsCgl0aXRsZSA9ICJEaWZmZXJlbmNlIGluIEluc3RhbGxhdGlvbiBZZWFycywgQ29tcGFyaW5nIENpdHkgRGF0YSBhbmQgVmVyaWZpZWQgRGF0YTogVmFuY291dmVyLCBDQSIsCglvdXRfZGF0YSA9IFRSVUUKKQoKIyBDYWxjIG1ldHJpY3MgZm9yIGRlc2NyaXB0aW9uCnNmaWc3X24gPC0gc3VtKHNmaWc3JGRhdGEkbikKc2ZpZzdfMCA8LSBzZmlnNyRkYXRhICU+JSAgIyBwZXJjIGNvcnJlY3QKCWZpbHRlcih5ZWFyX2RpZmYgPT0gMCkgJT4lCglwdWxsKG5fcGVyYykgJT4lCglyb3VuZCgxKQpzZmlnN19wbTEgPC0gc2ZpZzckZGF0YSAlPiUgIyBwZXJjIHBsdXMvbWludXMgMQoJZmlsdGVyKHllYXJfZGlmZiA+PSAtMSAmIHllYXJfZGlmZiA8PSAxKSAlPiUKCXB1bGwobl9wZXJjKSAlPiUKCXN1bSAlPiUKCXJvdW5kKDEpCmBgYAoKQW55IGRhdGEgd2hlcmUgYSBjaXR5IHByb3ZpZGVkIGFuZCB2ZXJpZmllZCBpbnN0YWxsYXRpb24geWVhcnMgd2VyZSBtaXNzaW5nIG9yIHRoZSB2ZXJpZmllZCB5ZWFyIG9jY3VycmVkIGVhcmxpZXIgdGhhbiB0aGUgc3RhcnQgb2YgdGhlIHN0dWR5IHBlcmlvZCAoMjAwOSkgaGFzIGJlZW4gZXhjbHVkZWQgZnJvbSBhbmFseXNpcywgeWllbGRpbmcgbj1gciBzZmlnN19uYCBzZWdtZW50cy4gVGhlIGdyYXBoIHNob3dzIHRoYXQgYHIgc2ZpZzdfMGAlIG9mIHRoZSBpbmNsdWRlZCBzZWdtZW50cyBoYWQgdGhlIGNvcnJlY3QgaW5zdGFsbGF0aW9uIHllYXIgYXMgcGVyIHRoZSBjaXR5J3MgZGF0YSwgYW5kIGByIHNmaWc3X3BtMWAlIHdlcmUgYWNjdXJhdGUgd2l0aGluIGEgcmFuZ2Ugb2YgwrExIHllYXIuCgojIyMgUGxvdAoKYGBge3IgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDV9CnNmaWc3JHBsb3QKYGBgCgojIyMgRGF0YQoKYGBge3J9CnNmaWc3JGRhdGEKYGBgCgojIyBTdXBwbGVtZW50YXJ5IEZpZ3VyZSA4OiBBIGNvbXBhcmF0aXZlIGFuYWx5c2lzIGJldHdlZW4gbXVuaWNpcGFsIGRhdGEgYW5kIHZlcmlmaWVkIGRhdGEgb24gdGhlIGluc3RhbGxhdGlvbiB5ZWFycyBmb3IgY3ljbGluZyBpbmZyYXN0cnVjdHVyZSBpbiBDYWxnYXJ5LCBDQS4gey50YWJzZXR9CgpgYGB7cn0KCiMgQ3JlYXRlIHRoZSBwbG90CnNmaWc4IDwtIHBsb3RfeWVhcmx5X2RpZmYoCgljYWxnLAoJdGl0bGUgPSAiRGlmZmVyZW5jZSBpbiBJbnN0YWxsYXRpb24gWWVhcnMsIENvbXBhcmluZyBDaXR5IERhdGEgYW5kIFZlcmlmaWVkIERhdGE6IENhbGdhcnksIENBIiwKCW91dF9kYXRhID0gVFJVRQopCgojIENhbGMgbWV0cmljcyBmb3IgZGVzY3JpcHRpb24Kc2ZpZzhfbiA8LSBzdW0oc2ZpZzgkZGF0YSRuKQpzZmlnOF8wIDwtIHNmaWc4JGRhdGEgJT4lICAjIHBlcmMgY29ycmVjdAoJZmlsdGVyKHllYXJfZGlmZiA9PSAwKSAlPiUKCXB1bGwobl9wZXJjKSAlPiUKCXJvdW5kKDEpCnNmaWc4X3BtMSA8LSBzZmlnOCRkYXRhICU+JSAjIHBlcmMgcGx1cy9taW51cyAxCglmaWx0ZXIoeWVhcl9kaWZmID49IC0xICYgeWVhcl9kaWZmIDw9IDEpICU+JQoJcHVsbChuX3BlcmMpICU+JQoJc3VtICU+JQoJcm91bmQoMSkKYGBgCgpBbnkgZGF0YSB3aGVyZSBhIGNpdHkgcHJvdmlkZWQgYW5kIHZlcmlmaWVkIGluc3RhbGxhdGlvbiB5ZWFycyB3ZXJlIG1pc3Npbmcgb3IgdGhlIHZlcmlmaWVkIHllYXIgb2NjdXJyZWQgZWFybGllciB0aGFuIHRoZSBzdGFydCBvZiB0aGUgc3R1ZHkgcGVyaW9kICgyMDA5KSBoYXMgYmVlbiBleGNsdWRlZCBmcm9tIGFuYWx5c2lzLCB5aWVsZGluZyBuPWByIHNmaWc4X25gIHNlZ21lbnRzLiBUaGUgZ3JhcGggc2hvd3MgdGhhdCBgciBzZmlnOF8wYCUgb2YgdGhlIGluY2x1ZGVkIHNlZ21lbnRzIGhhZCB0aGUgY29ycmVjdCBpbnN0YWxsYXRpb24geWVhciBhcyBwZXIgdGhlIGNpdHkncyBkYXRhLCBhbmQgYHIgc2ZpZzhfcG0xYCUgd2VyZSBhY2N1cmF0ZSB3aXRoaW4gYSByYW5nZSBvZiDCsTEgeWVhci4KCiMjIyBQbG90CgpgYGB7ciBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNX0Kc2ZpZzgkcGxvdApgYGAKCiMjIyBEYXRhCgpgYGB7cn0Kc2ZpZzgkZGF0YQpgYGAKCiMjIFN1cHBsZW1lbnRhcnkgRmlndXJlIDk6IEEgY29tcGFyYXRpdmUgYW5hbHlzaXMgYmV0d2VlbiBtdW5pY2lwYWwgZGF0YSBhbmQgdmVyaWZpZWQgZGF0YSBvbiB0aGUgaW5zdGFsbGF0aW9uIHllYXJzIGZvciBjeWNsaW5nIGluZnJhc3RydWN0dXJlIGluIFRvcm9udG8sIENBLiB7LnRhYnNldH0KCmBgYHtyfQoKIyBDcmVhdGUgdGhlIHBsb3QKc2ZpZzkgPC0gcGxvdF95ZWFybHlfZGlmZigKCXRvcm9uLAoJdGl0bGUgPSAiRGlmZmVyZW5jZSBpbiBJbnN0YWxsYXRpb24gWWVhcnMsIENvbXBhcmluZyBDaXR5IERhdGEgYW5kIFZlcmlmaWVkIERhdGE6IFRvcm9udG8sIENBIiwKCW91dF9kYXRhID0gVFJVRQopCgojIENhbGMgbWV0cmljcyBmb3IgZGVzY3JpcHRpb24Kc2ZpZzlfbiA8LSBzdW0oc2ZpZzkkZGF0YSRuKQpzZmlnOV8wIDwtIHNmaWc5JGRhdGEgJT4lICAjIHBlcmMgY29ycmVjdAoJZmlsdGVyKHllYXJfZGlmZiA9PSAwKSAlPiUKCXB1bGwobl9wZXJjKSAlPiUKCXJvdW5kKDEpCnNmaWc5X3BtMSA8LSBzZmlnOSRkYXRhICU+JSAjIHBlcmMgcGx1cy9taW51cyAxCglmaWx0ZXIoeWVhcl9kaWZmID49IC0xICYgeWVhcl9kaWZmIDw9IDEpICU+JQoJcHVsbChuX3BlcmMpICU+JQoJc3VtICU+JQoJcm91bmQoMSkKYGBgCgpBbnkgZGF0YSB3aGVyZSBhIGNpdHkgcHJvdmlkZWQgYW5kIHZlcmlmaWVkIGluc3RhbGxhdGlvbiB5ZWFycyB3ZXJlIG1pc3Npbmcgb3IgdGhlIHZlcmlmaWVkIHllYXIgb2NjdXJyZWQgZWFybGllciB0aGFuIHRoZSBzdGFydCBvZiB0aGUgc3R1ZHkgcGVyaW9kICgyMDA5KSBoYXMgYmVlbiBleGNsdWRlZCBmcm9tIGFuYWx5c2lzLCB5aWVsZGluZyBuPWByIHNmaWc5X25gIHNlZ21lbnRzLiBUaGUgZ3JhcGggc2hvd3MgdGhhdCBgciBzZmlnOV8wYCUgb2YgdGhlIGluY2x1ZGVkIHNlZ21lbnRzIGhhZCB0aGUgY29ycmVjdCBpbnN0YWxsYXRpb24geWVhciBhcyBwZXIgdGhlIGNpdHkncyBkYXRhLCBhbmQgYHIgc2ZpZzlfcG0xYCUgd2VyZSBhY2N1cmF0ZSB3aXRoaW4gYSByYW5nZSBvZiDCsTEgeWVhci4KCiMjIyBQbG90CgpgYGB7ciBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNX0Kc2ZpZzkkcGxvdApgYGAKCiMjIyBEYXRhCgpgYGB7cn0Kc2ZpZzkkZGF0YQpgYGAKCiMgQ29udHJpYnV0aW9ucwoKUmljaGFyZCBXZW4gZGV2ZWxvcGVkIHJlcHJvZHVjaWJsZSBSIGNvZGUgYW5kIG9yZ2FuaXplZCB0aGUgZGF0YSBiYXNlZCBvbiBLb25yYWQgU2Ftc2VsJ3MgZHJhZnQgbWFudXNjcmlwdCBhbmQgcHJldmlvdXMgUiBjb2RlLiBLb25yYWQgU2Ftc2VsIHByZXBhcmVkIGRyYWZ0IG1hbnVzY3JpcHQsIHJhdyBkYXRhLCBhbmQgcHJvdmlkZWQgY29uc3VsdGF0aW9uIG9uIGRhdGEgYW5kIG1ldGhvZHMuCgojIEFja25vd2xlZGdlbWVudHMKCkxpbmRhIFJvdGhtYW4gYW5kIEJyaWNlIEJhdG9tZW4gcHJvdmlkZWQgc3VwZXJ2aXNpb24sIHByb2plY3QgYWRtaW5pc3RyYXRpb24sIHJlc291cmNlcywgZnVuZGluZywgYW5kIHJldmlldy9lZGl0aW5nIGZvciB0aGUgZHJhZnQgbWFudXNjcmlwdC4K